aboutsummaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
authorMarcel Braun <marcel.braun@ost.ch>2026-03-28 19:16:27 +0100
committerMarcel Braun <marcel.braun@ost.ch>2026-03-28 19:16:27 +0100
commit2864e0b061f923a3c73c608b9c27ca4a7116e27c (patch)
tree7175be5fcaa789e0bfd6d0aeb4e7f6ac756cabf6 /libs
parent05269b10e50a80f557c2be475904ff15dc1bbec4 (diff)
parent8a9bf5a90b7f46d5c615b55a3fc418b419db4926 (diff)
downloadteachos-2864e0b061f923a3c73c608b9c27ca4a7116e27c.tar.xz
teachos-2864e0b061f923a3c73c608b9c27ca4a7116e27c.zip
Merge branch 'vfs' into 'develop-BA-FS26'
implement basic vfs See merge request teachos/kernel!16
Diffstat (limited to 'libs')
-rw-r--r--libs/kstd/CMakeLists.txt1
-rw-r--r--libs/kstd/include/kstd/bits/shared_ptr.hpp345
-rw-r--r--libs/kstd/include/kstd/string348
-rw-r--r--libs/kstd/tests/src/string.cpp445
4 files changed, 1102 insertions, 37 deletions
diff --git a/libs/kstd/CMakeLists.txt b/libs/kstd/CMakeLists.txt
index 06543ab..d4f415f 100644
--- a/libs/kstd/CMakeLists.txt
+++ b/libs/kstd/CMakeLists.txt
@@ -46,6 +46,7 @@ if(NOT CMAKE_CROSSCOMPILING)
add_executable("kstd_tests"
"tests/src/vector.cpp"
"tests/src/os_panic.cpp"
+ "tests/src/string.cpp"
)
target_include_directories("kstd_tests" PRIVATE
diff --git a/libs/kstd/include/kstd/bits/shared_ptr.hpp b/libs/kstd/include/kstd/bits/shared_ptr.hpp
index 6bce83f..8930095 100644
--- a/libs/kstd/include/kstd/bits/shared_ptr.hpp
+++ b/libs/kstd/include/kstd/bits/shared_ptr.hpp
@@ -10,10 +10,200 @@
namespace kstd
{
+ /**
+ * @brief Control block for shared_ptr and weak_ptr. This control block contains the reference counts for shared
+ * ownership and weak ownership. The shared_count tracks the number of shared_ptr instances that own the managed
+ * object, while the weak_count tracks the number of weak_ptr instances that reference the managed object.
+ * The weak_count is needed to determine when it is safe to delete the shared_control_block itself
+ */
+ struct shared_control_block
+ {
+ std::atomic<std::size_t> shared_count;
+ std::atomic<std::size_t> weak_count;
+
+ explicit shared_control_block(std::size_t shared = 1, std::size_t weak = 0)
+ : shared_count(shared)
+ , weak_count(weak)
+ {}
+ };
+
template<typename T>
struct shared_ptr;
/**
+ * @brief weak_ptr is a smart pointer that holds a non-owning weak reference to an object that is managed by
+ * shared_ptr. It must be converted to shared_ptr in to be able to access the referenced object. A weak_ptr is created
+ * as a copy of a shared_ptr, or as a copy of another weak_ptr. A weak_ptr is typically used to break circular
+ * references between shared_ptr to avoid memory leaks. A weak_ptr does not contribute to the reference count of the
+ * object, and it does not manage the lifetime of the object. If the object managed by shared_ptr is destroyed, the
+ * weak_ptr becomes expired and cannot be used to access the object anymore.
+ */
+ template<typename T>
+ struct weak_ptr
+ {
+ template<typename U>
+ friend struct shared_ptr;
+
+ /**
+ * @brief Constructs a null weak_ptr.
+ */
+ weak_ptr() noexcept
+ : pointer(nullptr)
+ , control(nullptr)
+ {}
+
+ template<typename U>
+ requires(std::is_convertible_v<U *, T *>)
+ weak_ptr(shared_ptr<U> const & other)
+ : pointer(other.pointer)
+ , control(other.control)
+ {
+ if (control != nullptr)
+ {
+ ++(control->weak_count);
+ }
+ }
+
+ /**
+ * @brief Copy constructor. Constructs a weak_ptr which shares ownership of the object managed by other.
+ */
+ weak_ptr(weak_ptr const & other)
+ : pointer(other.pointer)
+ , control(other.control)
+ {
+ if (control != nullptr)
+ {
+ ++(control->weak_count);
+ }
+ }
+
+ /**
+ * @brief Move constructor. Constructs a weak_ptr which takes ownership of the object managed by other.
+ */
+ weak_ptr(weak_ptr && other) noexcept
+ : pointer(other.pointer)
+ , control(other.control)
+ {
+ other.pointer = nullptr;
+ other.control = nullptr;
+ }
+
+ /**
+ * @brief Assignment operator. Assigns the weak_ptr to another weak_ptr.
+ */
+ auto operator=(weak_ptr const & other) -> weak_ptr &
+ {
+ if (this != &other)
+ {
+ cleanup();
+ pointer = other.pointer;
+ control = other.control;
+ if (control != nullptr)
+ {
+ ++(control->weak_count);
+ }
+ }
+
+ return *this;
+ }
+
+ /**
+ * @brief Move assignment operator. Move-assigns a weak_ptr from other.
+ */
+ auto operator=(weak_ptr && other) noexcept -> weak_ptr &
+ {
+ if (this != &other)
+ {
+ cleanup();
+ pointer = other.pointer;
+ control = other.control;
+ other.pointer = nullptr;
+ other.control = nullptr;
+ }
+
+ return *this;
+ }
+
+ /**
+ * @brief Destructor. Cleans up resources if necessary.
+ */
+ ~weak_ptr()
+ {
+ cleanup();
+ }
+
+ /**
+ * @brief Returns a shared_ptr that shares ownership of the managed object if the managed object still exists, or an
+ * empty shared_ptr otherwise.
+ */
+ [[nodiscard]] auto lock() const -> shared_ptr<T>
+ {
+ return shared_ptr<T>(*this);
+ }
+
+ private:
+ auto cleanup() -> void
+ {
+ if (control != nullptr)
+ {
+ if (--(control->weak_count) == 0 && control->shared_count == 0)
+ {
+ delete control;
+ }
+ }
+ }
+
+ T * pointer;
+ shared_control_block * control;
+ };
+
+ /**
+ * @brief enable_shared_from_this is a base class that allows an object that is currently managed by a shared_ptr to
+ * create additional shared_ptr instances that share ownership of the same object. This is usefl when you want to
+ * create shared_ptr instances in a member function of the object.
+ *
+ * @tparam T The type of the managed object.
+ */
+ template<typename T>
+ struct enable_shared_from_this
+ {
+ template<typename U>
+ friend struct shared_ptr;
+
+ friend T;
+
+ public:
+ /**
+ * @brief Returns a shared_ptr that shares ownership of *this.
+ */
+ auto shared_from_this() -> shared_ptr<T>
+ {
+ return shared_ptr<T>(weak_this);
+ }
+
+ /**
+ * @brief Returns a shared_ptr that shares ownership of *this.
+ */
+ auto shared_from_this() const -> shared_ptr<T const>
+ {
+ return shared_ptr<T const>(weak_this);
+ }
+
+ private:
+ enable_shared_from_this() = default;
+ enable_shared_from_this(enable_shared_from_this const &) = default;
+ auto operator=(enable_shared_from_this const &) -> enable_shared_from_this & = default;
+ ~enable_shared_from_this() = default;
+
+ void internal_assign_ptr(shared_ptr<T> const & ptr) const
+ {
+ weak_this = ptr;
+ }
+
+ mutable weak_ptr<T> weak_this{}; ///< Weak pointer to the object, used for shared_from_this functionality.
+ };
+
+ /**
* @brief Shared_pointer is a smart pointer that retains shared ownership of an object through a pointer. Several
* shared_ptr objects may own the same object. The object is destroyed and its memory deallocated when either of
* the following happens: the last remaining shared_ptr owning the object is destroyed; the last remaining
@@ -31,12 +221,23 @@ namespace kstd
template<typename U>
friend struct shared_ptr;
+ template<typename U>
+ friend struct weak_ptr;
+
+ /**
+ * @brief Construct an empty shared_ptr.
+ */
+ shared_ptr() noexcept
+ : pointer(nullptr)
+ , control(nullptr)
+ {}
+
/**
* @brief Construct an empty shared_ptr from nullptr.
*/
shared_ptr(std::nullptr_t) noexcept
: pointer(nullptr)
- , ref_count(nullptr)
+ , control(nullptr)
{}
/**
@@ -44,11 +245,33 @@ namespace kstd
*
* @param pointer A pointer to an object to manage (default is nullptr).
*/
- explicit shared_ptr(T * pointer = nullptr)
+ template<typename U>
+ requires(std::is_convertible_v<U *, T *>)
+ explicit shared_ptr(U * pointer = nullptr)
: pointer(pointer)
- , ref_count(pointer != nullptr ? new std::atomic<std::size_t>(1) : nullptr)
+ , control(pointer != nullptr ? new shared_control_block() : nullptr)
+ {
+ assign_enable_shared_from_this(pointer);
+ }
+
+ /**
+ * @brief Constructor a shared_ptr from a weak_ptr. If other is not expired, constructs a shared_ptr which shares
+ * ownership of the object managed by other. Otherwise, constructs an empty shared_ptr.
+ *
+ * @param other The weak_ptr to construct from.
+ */
+ template<typename U>
+ requires(std::is_convertible_v<U *, T *>)
+ explicit shared_ptr(weak_ptr<U> const & other)
+ : pointer(nullptr)
+ , control(nullptr)
{
- // Nothing to do.
+ if (other.control != nullptr && other.control->shared_count != 0)
+ {
+ pointer = other.pointer;
+ control = other.control;
+ ++(control->shared_count);
+ }
}
/**
@@ -58,11 +281,11 @@ namespace kstd
*/
shared_ptr(shared_ptr const & other)
: pointer(other.pointer)
- , ref_count(other.ref_count)
+ , control(other.control)
{
- if (ref_count != nullptr)
+ if (control != nullptr)
{
- ++(*ref_count);
+ ++(control->shared_count);
}
}
@@ -76,11 +299,11 @@ namespace kstd
requires(std::is_convertible_v<U *, T *>)
shared_ptr(shared_ptr<U> const & other)
: pointer(other.pointer)
- , ref_count(other.ref_count)
+ , control(other.control)
{
- if (ref_count != nullptr)
+ if (control != nullptr)
{
- ++(*ref_count);
+ ++(control->shared_count);
}
}
@@ -91,10 +314,10 @@ namespace kstd
*/
shared_ptr(shared_ptr && other) noexcept
: pointer(other.pointer)
- , ref_count(other.ref_count)
+ , control(other.control)
{
other.pointer = nullptr;
- other.ref_count = nullptr;
+ other.control = nullptr;
}
/**
@@ -107,10 +330,10 @@ namespace kstd
requires(std::is_convertible_v<U *, T *>)
shared_ptr(shared_ptr<U> && other) noexcept
: pointer(other.pointer)
- , ref_count(other.ref_count)
+ , control(other.control)
{
other.pointer = nullptr;
- other.ref_count = nullptr;
+ other.control = nullptr;
}
/**
@@ -127,11 +350,11 @@ namespace kstd
{
cleanup();
pointer = other.pointer;
- ref_count = other.ref_count;
+ control = other.control;
- if (ref_count != nullptr)
+ if (control != nullptr)
{
- ++(*ref_count);
+ ++(control->shared_count);
}
}
@@ -151,11 +374,11 @@ namespace kstd
{
cleanup();
pointer = other.pointer;
- ref_count = other.ref_count;
+ control = other.control;
- if (ref_count != nullptr)
+ if (control != nullptr)
{
- ++(*ref_count);
+ ++(control->shared_count);
}
return *this;
@@ -174,9 +397,9 @@ namespace kstd
{
cleanup();
pointer = other.pointer;
- ref_count = other.ref_count;
+ control = other.control;
other.pointer = nullptr;
- other.ref_count = nullptr;
+ other.control = nullptr;
}
return *this;
@@ -195,9 +418,9 @@ namespace kstd
{
cleanup();
pointer = other.pointer;
- ref_count = other.ref_count;
+ control = other.control;
other.pointer = nullptr;
- other.ref_count = nullptr;
+ other.control = nullptr;
return *this;
}
@@ -209,7 +432,7 @@ namespace kstd
{
cleanup();
pointer = nullptr;
- ref_count = nullptr;
+ control = nullptr;
return *this;
}
@@ -230,7 +453,8 @@ namespace kstd
{
cleanup();
pointer = ptr;
- ref_count = ptr != nullptr ? new std::atomic<std::size_t>(1) : nullptr;
+ control = ptr != nullptr ? new shared_control_block() : nullptr;
+ assign_enable_shared_from_this(ptr);
}
/**
@@ -242,7 +466,7 @@ namespace kstd
void swap(shared_ptr & other)
{
std::swap(pointer, other.pointer);
- std::swap(ref_count, other.ref_count);
+ std::swap(control, other.control);
}
/**
@@ -288,9 +512,9 @@ namespace kstd
*/
[[nodiscard]] auto use_count() const -> std::size_t
{
- if (ref_count != nullptr)
+ if (control != nullptr)
{
- return *ref_count;
+ return control->shared_count;
}
return 0;
@@ -322,26 +546,49 @@ namespace kstd
return ptr.pointer == nullptr;
}
+ private:
/**
- * @brief Defaulted three-way comparator operator.
+ * @brief If the candidate type inherits from enable_shared_from_this, assigns the internal weak pointer to this
+ * shared_ptr. This weak_ptr is used to implement shared_from_this functionality for the candidate type. If the
+ * candidate type does not inherit from enable_shared_from_this, this function does nothing.
+ *
+ * @tparam U The candidate type to check for enable_shared_from_this inheritance.
+ * @param candidate The candidate object to assign the internal weak pointer for.
*/
- [[nodiscard]] auto operator<=>(shared_ptr const & other) const = default;
+ template<typename U>
+ auto assign_enable_shared_from_this(U * candidate) -> void
+ {
+ if constexpr (requires(U * p, shared_ptr<T> const & sp) { p->internal_assign_ptr(sp); })
+ {
+ if (candidate != nullptr)
+ {
+ candidate->internal_assign_ptr(*this);
+ }
+ }
+ }
- private:
/**
* @brief Releases ownership and deletes the object if this was the last reference to the owned managed object.
*/
auto cleanup() -> void
{
- if (ref_count != nullptr && --(*ref_count) == 0)
+ if (control != nullptr)
{
- delete pointer;
- delete ref_count;
+ if (--(control->shared_count) == 0)
+ {
+ delete pointer;
+ pointer = nullptr;
+
+ if (control->weak_count == 0)
+ {
+ delete control;
+ }
+ }
}
}
- T * pointer; ///< The managed object.
- std::atomic<std::size_t> * ref_count; ///< Reference count.
+ T * pointer; ///< The managed object.
+ shared_control_block * control; ///< Shared control block.
};
/**
@@ -372,6 +619,30 @@ namespace kstd
{
return shared_ptr<T>(new T(std::forward<Args>(args)...));
}
+
+ /**
+ * @brief Equality operator for shared_ptr. Two shared_ptr instances are equal if they point to the same object
+ * @tparam T, U Types of the managed objects of the shared_ptr instances being compared.
+ * @param lhs, rhs The shared_ptr instances to compare.
+ * @return true if lhs and rhs point to the same object, false otherwise.
+ */
+ template<typename T, typename U>
+ [[nodiscard]] auto inline operator==(shared_ptr<T> const & lhs, shared_ptr<U> const & rhs) -> bool
+ {
+ return lhs.get() == rhs.get();
+ }
+
+ /**
+ * @brief Three-way comparison operator for shared_ptr. Compares the stored pointers of lhs and rhs using operator<=>.
+ * @tparam T, U Types of the managed objects of the shared_ptr instances being compared.
+ * @param lhs, rhs The shared_ptr instances to compare.
+ * @return The result of comparing the stored pointers of lhs and rhs using operator<=>
+ */
+ template<typename T, typename U>
+ [[nodiscard]] auto inline operator<=>(shared_ptr<T> const & lhs, shared_ptr<U> const & rhs)
+ {
+ return lhs.get() <=> rhs.get();
+ }
} // namespace kstd
#endif \ No newline at end of file
diff --git a/libs/kstd/include/kstd/string b/libs/kstd/include/kstd/string
new file mode 100644
index 0000000..075422e
--- /dev/null
+++ b/libs/kstd/include/kstd/string
@@ -0,0 +1,348 @@
+#ifndef KSTD_STRING_HPP
+#define KSTD_STRING_HPP
+
+#include <kstd/cstring>
+#include <kstd/os/error.hpp>
+#include <kstd/vector>
+
+#include <algorithm>
+#include <concepts>
+#include <cstddef>
+#include <string_view>
+
+namespace kstd
+{
+ /**
+ * @brief A simple string implementation that owns its data and provides basic operations.
+ */
+ struct string
+ {
+ //! The type of the characters contained in this string.
+ using value_type = char;
+ //! The type of the underlying storage used by this string.
+ using storage_type = kstd::vector<value_type>;
+ //! The type of all sizes used in and with this string.
+ using size_type = std::size_t;
+ //! The type of the difference between two iterators.
+ using difference_type = std::ptrdiff_t;
+ //! The type of references to single values in this string.
+ using reference = value_type &;
+ //! The type of references to constant single values in this string.
+ using const_reference = value_type const &;
+ //! The type of pointers to single values in this string.
+ using pointer = value_type *;
+ //! The type of pointers to constant single values in this string.
+ using const_pointer = value_type const *;
+ //! The type of iterators into this string.
+ using iterator = pointer;
+ //! The type of constant iterators into this string.
+ using const_iterator = const_pointer;
+
+ /**
+ * @brief Constructs an empty null-terminated string.
+ */
+ string()
+ : m_storage{value_type{'\0'}}
+ {}
+
+ /**
+ * @brief Constructs a string from a string view by copying the characters into owned storage.
+ * @param view The string view to copy the characters from.
+ */
+ string(std::string_view view)
+ : string()
+ {
+ append(view);
+ }
+
+ /**
+ * @brief Constructs a string from a null-terminated C-style string by copying the characters into owned storage.
+ * @param c_str The null-terminated C-style string to copy.
+ */
+ string(char const * c_str)
+ : string()
+ {
+ if (c_str != nullptr)
+ {
+ append(std::string_view{c_str});
+ }
+ }
+
+ /**
+ * @brief Constructs a string containing a single character.
+ * @param c The character to copy.
+ */
+ string(value_type c)
+ : string()
+ {
+ push_back(c);
+ }
+
+ /**
+ * @brief Constructs a string by copying another string.
+ * @param other The string to copy.
+ */
+ constexpr string(string const & other)
+ : m_storage{other.m_storage}
+ {}
+
+ /**
+ * @brief Destructs the string.
+ */
+ constexpr ~string() = default;
+
+ /**
+ * @brief Assigns the value of another string to this string.
+ * @param other The string to assign from.
+ * @return A reference to this string.
+ */
+ constexpr auto operator=(string const & other) -> string & = default;
+
+ /**
+ * @brief Returns the number of characters in this string, not including the null terminator.
+ */
+ [[nodiscard]] constexpr auto size() const noexcept -> size_type
+ {
+ return m_storage.empty() ? 0 : m_storage.size() - 1;
+ }
+
+ /**
+ * @brief Checks if this string is empty, not including the null terminator.
+ */
+ [[nodiscard]] constexpr auto empty() const noexcept -> bool
+ {
+ return size() == 0;
+ }
+
+ /**
+ * @brief Clears the content of the string, resulting in an empty string.
+ * The string remains null-terminated after this operation.
+ */
+ constexpr auto clear() -> void
+ {
+ m_storage.clear();
+ m_storage.push_back(value_type{'\0'});
+ }
+
+ //! Get a pointer to the underlying storage of the string
+ [[nodiscard]] constexpr auto data() noexcept -> pointer
+ {
+ return m_storage.data();
+ }
+
+ //! Get a const pointer to the underlying storage of the string
+ [[nodiscard]] constexpr auto data() const noexcept -> const_pointer
+ {
+ return m_storage.data();
+ }
+
+ //! Get a const pointer to the underlying storage of the string
+ [[nodiscard]] constexpr auto c_str() const noexcept -> const_pointer
+ {
+ return data();
+ }
+
+ //! Get an iterator to the beginning of the string
+ [[nodiscard]] constexpr auto begin() noexcept -> iterator
+ {
+ return data();
+ }
+
+ //! Get an const iterator to the beginning of the string
+ [[nodiscard]] constexpr auto begin() const noexcept -> const_iterator
+ {
+ return data();
+ }
+
+ //! Get an const iterator to the beginning of the string
+ [[nodiscard]] constexpr auto cbegin() const noexcept -> const_iterator
+ {
+ return begin();
+ }
+
+ //! Get an iterator to the end of the string
+ [[nodiscard]] constexpr auto end() noexcept -> iterator
+ {
+ return data() + size();
+ }
+
+ //! Get an const iterator to the end of the string
+ [[nodiscard]] constexpr auto end() const noexcept -> const_iterator
+ {
+ return data() + size();
+ }
+
+ //! Get an const iterator to the end of the string
+ [[nodiscard]] constexpr auto cend() const noexcept -> const_iterator
+ {
+ return end();
+ }
+
+ //! Get a reference to the first character of the string
+ [[nodiscard]] constexpr auto front() -> reference
+ {
+ return m_storage.front();
+ }
+
+ //! Get a const reference to the first character of the string
+ [[nodiscard]] constexpr auto front() const -> const_reference
+ {
+ return m_storage.front();
+ }
+
+ //! Get a reference to the last character of the string
+ [[nodiscard]] constexpr auto back() -> reference
+ {
+ return m_storage[size() - 1];
+ }
+
+ //! Get a const reference to the last character of the string
+ [[nodiscard]] constexpr auto back() const -> const_reference
+ {
+ return m_storage[size() - 1];
+ }
+
+ /**
+ * @brief Appends a character to the end of the string.
+ * @param ch The character to append.
+ */
+ constexpr auto push_back(value_type ch) -> void
+ {
+ m_storage.back() = ch;
+ m_storage.push_back(value_type{'\0'});
+ }
+
+ /**
+ * @brief Appends a string view to the end of the string by copying the characters into owned storage.
+ * @param view The string view to append.
+ * @return A reference to this string.
+ */
+ constexpr auto append(std::string_view view) -> string &
+ {
+ if (!view.empty())
+ {
+ std::ranges::for_each(view, [this](auto const ch) { push_back(ch); });
+ }
+
+ return *this;
+ }
+
+ /**
+ * @brief Appends another string to the end of this string by copying the characters into owned storage.
+ * @param other The string to append.
+ * @return A reference to this string.
+ */
+ constexpr auto append(string const & other) -> string &
+ {
+ return append(other.view());
+ }
+
+ /**
+ * @brief Appends another string to the end of this string by copying the characters into owned storage.
+ * @param other The string to append.
+ * @return A reference to this string.
+ */
+ constexpr auto operator+=(string const & other) -> string &
+ {
+ return append(other);
+ }
+
+ /**
+ * @brief Appends a character to the end of the string.
+ * @param ch The character to append.
+ * @return A reference to this string.
+ */
+ constexpr auto operator+=(value_type ch) -> string &
+ {
+ push_back(ch);
+ return *this;
+ }
+
+ /**
+ * @brief Returns a string view of this string, which is a non-owning view into the characters of this string.
+ */
+ [[nodiscard]] constexpr auto view() const noexcept -> std::string_view
+ {
+ return std::string_view{data(), size()};
+ }
+
+ private:
+ //! The underlying storage of the string, which owns the characters and ensures null-termination.
+ storage_type m_storage{};
+ };
+
+ /**
+ * @brief Concatenates a strings and a character and returns the result as a new string.
+ * @param lhs The string to concatenate.
+ * @param rhs The string to concatenate.
+ * @return A new string that is the result of concatenating @p lhs and @p rhs.
+ */
+ [[nodiscard]] constexpr auto inline operator+(string const & lhs, string const & rhs) -> string
+ {
+ string result{lhs};
+ result += rhs;
+ return result;
+ }
+
+ /**
+ * @brief Converts an unsigned integer to a string by converting each digit to the corresponding character and
+ * concatenating them.
+ * @tparam N The type of the unsigned integer to convert.
+ * @param value The unsigned integer to convert.
+ * @return A string representation of the given unsigned integer.
+ */
+ template<typename N>
+ requires std::unsigned_integral<N>
+ [[nodiscard]] constexpr auto inline to_string(N value) -> string
+ {
+ if (value == 0)
+ {
+ return "0";
+ }
+
+ string result;
+
+ while (value > 0)
+ {
+ char const digit = '0' + (value % 10);
+ result.push_back(digit);
+ value /= 10;
+ }
+
+ std::reverse(result.begin(), result.end());
+ return result;
+ }
+
+ [[nodiscard]] constexpr auto inline operator==(string const & lhs, string const & rhs) -> bool
+ {
+ return lhs.view() == rhs.view();
+ }
+
+ [[nodiscard]] constexpr auto inline operator!=(string const & lhs, string const & rhs) -> bool
+ {
+ return !(lhs == rhs);
+ }
+
+ [[nodiscard]] constexpr auto inline operator==(string const & lhs, std::string_view rhs) -> bool
+ {
+ return lhs.view() == rhs;
+ }
+
+ [[nodiscard]] constexpr auto inline operator!=(string const & lhs, std::string_view rhs) -> bool
+ {
+ return !(lhs == rhs);
+ }
+
+ [[nodiscard]] constexpr auto inline operator==(std::string_view lhs, string const & rhs) -> bool
+ {
+ return lhs == rhs.view();
+ }
+
+ [[nodiscard]] constexpr auto inline operator!=(std::string_view lhs, string const & rhs) -> bool
+ {
+ return !(lhs == rhs);
+ }
+
+} // namespace kstd
+
+#endif \ No newline at end of file
diff --git a/libs/kstd/tests/src/string.cpp b/libs/kstd/tests/src/string.cpp
new file mode 100644
index 0000000..43e9a6b
--- /dev/null
+++ b/libs/kstd/tests/src/string.cpp
@@ -0,0 +1,445 @@
+#include <kstd/string>
+
+#include <catch2/catch_test_macros.hpp>
+
+#include <algorithm>
+#include <ranges>
+#include <string_view>
+
+SCENARIO("String initialization and construction", "[string]")
+{
+ GIVEN("Nothing")
+ {
+ WHEN("constructing an empty string")
+ {
+ auto str = kstd::string{};
+
+ THEN("the size is zero and therefore the string is empty")
+ {
+ REQUIRE(str.empty());
+ REQUIRE(str.size() == 0);
+ }
+
+ THEN("the string is empty")
+ {
+ REQUIRE(str.view() == std::string_view{});
+ }
+ }
+ }
+
+ GIVEN("A string view")
+ {
+ auto view = std::string_view{"Blub Blub"};
+
+ WHEN("constructing a string from string_view")
+ {
+ auto str = kstd::string{view};
+
+ THEN("the string is not empty and has the same size as the view")
+ {
+ REQUIRE(!str.empty());
+ REQUIRE(str.size() == view.size());
+ }
+
+ THEN("the string contains the same characters as the view")
+ {
+ REQUIRE(str.view() == view);
+ }
+ }
+ }
+
+ GIVEN("A C-style string")
+ {
+ auto c_str = "Blub Blub";
+
+ WHEN("constructing a string from the C-style string")
+ {
+ auto str = kstd::string{c_str};
+
+ THEN("the string is not empty and has the same size as the C-style string")
+ {
+ REQUIRE(!str.empty());
+ REQUIRE(str.size() == strlen(c_str));
+ }
+
+ THEN("the string contains the same characters as the C-style string")
+ {
+ REQUIRE(str.view() == c_str);
+ }
+ }
+ }
+
+ GIVEN("A character")
+ {
+ auto ch = 'x';
+
+ WHEN("constructing a string from the character")
+ {
+ auto str = kstd::string{ch};
+
+ THEN("the string is not empty and has size 1")
+ {
+ REQUIRE(!str.empty());
+ REQUIRE(str.size() == 1);
+ }
+
+ THEN("the string contains the same character as the given character")
+ {
+ REQUIRE(str.view() == std::string_view{&ch, 1});
+ }
+ }
+ }
+
+ GIVEN("Another string")
+ {
+ auto other = kstd::string{"Blub Blub"};
+
+ WHEN("copy constructing a new string")
+ {
+ auto str = kstd::string{other};
+
+ THEN("the new string contains the same characters as the original")
+ {
+ REQUIRE(str.view() == other.view());
+ }
+ }
+
+ auto str = kstd::string{"Blub"};
+
+ WHEN("copy assigning another string")
+ {
+ auto other = kstd::string{"Blub Blub"};
+ str = other;
+
+ THEN("the string contains the same characters as the assigned string")
+ {
+ REQUIRE(str.view() == other.view());
+ }
+ }
+ }
+
+ GIVEN("A string")
+ {
+ auto str = kstd::string{"Hello"};
+
+ WHEN("copy assigning a string view")
+ {
+ auto view = std::string_view{"Hello, world!"};
+ str = view;
+
+ THEN("the string contains the same characters as the assigned view")
+ {
+ REQUIRE(str.view() == view);
+ }
+ }
+ }
+
+ GIVEN("A string")
+ {
+ auto str = kstd::string{"Hello"};
+
+ WHEN("copy assigning a C-style string")
+ {
+ auto c_str = "Hello, world!";
+ str = c_str;
+
+ THEN("the string contains the same characters as the assigned C-style string")
+ {
+ REQUIRE(str.view() == c_str);
+ }
+ }
+ }
+}
+
+SCENARIO("String concatenation", "[string]")
+{
+ GIVEN("Two strings")
+ {
+ auto str1 = kstd::string{"Blub"};
+ auto str2 = kstd::string{" Blub"};
+
+ WHEN("appending the second string to the first string")
+ {
+ str1.append(str2);
+
+ THEN("the first string contains the characters of both strings concatenated")
+ {
+ REQUIRE(str1.view() == "Blub Blub");
+ }
+
+ THEN("the size of the first string is the sum of the sizes of both strings")
+ {
+ REQUIRE(str1.size() == str2.size() + 4);
+ }
+ }
+
+ WHEN("using operator+= to append the second string to the first string")
+ {
+ str1 += str2;
+
+ THEN("the first string contains the characters of both strings concatenated")
+ {
+ REQUIRE(str1.view() == "Blub Blub");
+ }
+
+ THEN("the size of the first string is the sum of the sizes of both strings")
+ {
+ REQUIRE(str1.size() == str2.size() + 4);
+ }
+ }
+
+ WHEN("using operator+ to concatenate the two strings into a new string")
+ {
+ auto str3 = str1 + str2;
+
+ THEN("the new string contains the characters of both strings concatenated")
+ {
+ REQUIRE(str3.view() == "Blub Blub");
+ }
+
+ THEN("the size of the new string is the sum of the sizes of both strings")
+ {
+ REQUIRE(str3.size() == str1.size() + str2.size());
+ }
+ }
+ }
+
+ GIVEN("A string and a string view")
+ {
+ auto str = kstd::string{"Blub"};
+ auto view = std::string_view{" Blub"};
+
+ WHEN("appending the string view to the string")
+ {
+ str.append(view);
+
+ THEN("the string contains the characters of both the original string and the appended view concatenated")
+ {
+ REQUIRE(str.view() == "Blub Blub");
+ }
+
+ THEN("the size of the string is the sum of the sizes of the original string and the appended view")
+ {
+ REQUIRE(str.size() == view.size() + 4);
+ }
+ }
+ }
+
+ GIVEN("A string and a character")
+ {
+ auto str = kstd::string{"Blub"};
+ auto ch = '!';