/src/serenity/AK/StringBase.h
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2023, Dan Klishch <danilklishch@gmail.com> |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #pragma once |
8 | | |
9 | | #include <AK/Badge.h> |
10 | | #include <AK/Endian.h> |
11 | | #include <AK/Forward.h> |
12 | | |
13 | | namespace AK::Detail { |
14 | | |
15 | | class StringData; |
16 | | |
17 | | static constexpr size_t MAX_SHORT_STRING_BYTE_COUNT = sizeof(StringData*) - 1; |
18 | | |
19 | | struct ShortString { |
20 | | ReadonlyBytes bytes() const; |
21 | | size_t byte_count() const; |
22 | | |
23 | | // NOTE: This is the byte count shifted left 1 step and or'ed with a 1 (the SHORT_STRING_FLAG) |
24 | | u8 byte_count_and_short_string_flag { 0 }; |
25 | | u8 storage[MAX_SHORT_STRING_BYTE_COUNT] = { 0 }; |
26 | | }; |
27 | | |
28 | | static_assert(HostIsLittleEndian, "Order of fields in ShortString assumes LE."); |
29 | | static_assert(sizeof(ShortString) >= sizeof(StringData*)); |
30 | | static_assert(__builtin_offsetof(ShortString, byte_count_and_short_string_flag) == 0); |
31 | | |
32 | | class StringBase { |
33 | | public: |
34 | | // Creates an empty (zero-length) String. |
35 | | constexpr StringBase() |
36 | 36.4M | : StringBase(ShortString { SHORT_STRING_FLAG, {} }) |
37 | 36.4M | { |
38 | 36.4M | } |
39 | | |
40 | | StringBase(StringBase const&); |
41 | | |
42 | | constexpr StringBase(StringBase&& other) |
43 | 761M | : m_short_string(other.m_short_string) |
44 | 761M | { |
45 | 761M | other.m_short_string = ShortString {}; |
46 | 761M | other.m_short_string.byte_count_and_short_string_flag = SHORT_STRING_FLAG; |
47 | 761M | } |
48 | | |
49 | | StringBase& operator=(StringBase&&); |
50 | | StringBase& operator=(StringBase const&); |
51 | | |
52 | | constexpr ~StringBase() |
53 | 1.62G | { |
54 | 1.62G | if (!is_constant_evaluated()) |
55 | 1.62G | destroy_string(); |
56 | 1.62G | } |
57 | | |
58 | | // NOTE: This is primarily interesting to unit tests. |
59 | | [[nodiscard]] constexpr bool is_short_string() const |
60 | 2.93G | { |
61 | 2.93G | return (m_short_string.byte_count_and_short_string_flag & SHORT_STRING_FLAG) != 0; |
62 | 2.93G | } |
63 | | |
64 | | // Returns the underlying UTF-8 encoded bytes. |
65 | | // NOTE: There is no guarantee about null-termination. |
66 | | [[nodiscard]] ReadonlyBytes bytes() const; |
67 | | [[nodiscard]] u32 hash() const; |
68 | | [[nodiscard]] size_t byte_count() const; |
69 | | |
70 | | [[nodiscard]] bool operator==(StringBase const&) const; |
71 | | |
72 | 0 | [[nodiscard]] ALWAYS_INLINE FlatPtr raw(Badge<FlyString>) const { return bit_cast<FlatPtr>(m_data); } |
73 | | |
74 | | protected: |
75 | | template<typename Func> |
76 | | ErrorOr<void> replace_with_new_string(size_t byte_count, Func&& callback) |
77 | 29.2M | { |
78 | 29.2M | Bytes buffer = TRY(replace_with_uninitialized_buffer(byte_count)); |
79 | 29.2M | if (byte_count != 0) |
80 | 18.2M | TRY(callback(buffer)); |
81 | 29.2M | return {}; |
82 | 29.2M | } String.cpp:AK::ErrorOr<void, AK::Error> AK::Detail::StringBase::replace_with_new_string<AK::String::from_utf8_without_validation(AK::Span<unsigned char const>)::$_0>(unsigned long, AK::String::from_utf8_without_validation(AK::Span<unsigned char const>)::$_0&&) Line | Count | Source | 77 | 9.85M | { | 78 | 9.85M | Bytes buffer = TRY(replace_with_uninitialized_buffer(byte_count)); | 79 | 9.85M | if (byte_count != 0) | 80 | 177k | TRY(callback(buffer)); | 81 | 9.85M | return {}; | 82 | 9.85M | } |
String.cpp:AK::ErrorOr<void, AK::Error> AK::Detail::StringBase::replace_with_new_string<AK::String::from_utf8(AK::StringView)::$_0>(unsigned long, AK::String::from_utf8(AK::StringView)::$_0&&) Line | Count | Source | 77 | 19.4M | { | 78 | 19.4M | Bytes buffer = TRY(replace_with_uninitialized_buffer(byte_count)); | 79 | 19.4M | if (byte_count != 0) | 80 | 18.0M | TRY(callback(buffer)); | 81 | 19.4M | return {}; | 82 | 19.4M | } |
String.cpp:AK::ErrorOr<void, AK::Error> AK::Detail::StringBase::replace_with_new_string<AK::String::from_stream(AK::Stream&, unsigned long)::$_0>(unsigned long, AK::String::from_stream(AK::Stream&, unsigned long)::$_0&&) Line | Count | Source | 77 | 9.18k | { | 78 | 9.18k | Bytes buffer = TRY(replace_with_uninitialized_buffer(byte_count)); | 79 | 9.18k | if (byte_count != 0) | 80 | 437 | TRY(callback(buffer)); | 81 | 9.18k | return {}; | 82 | 9.18k | } |
Unexecuted instantiation: String.cpp:AK::ErrorOr<void, AK::Error> AK::Detail::StringBase::replace_with_new_string<AK::String::repeated(unsigned int, unsigned long)::$_1>(unsigned long, AK::String::repeated(unsigned int, unsigned long)::$_1&&) Unexecuted instantiation: String.cpp:AK::ErrorOr<void, AK::Error> AK::Detail::StringBase::replace_with_new_string<AK::String::repeated(AK::String const&, unsigned long)::$_0>(unsigned long, AK::String::repeated(AK::String const&, unsigned long)::$_0&&) |
83 | | |
84 | | template<typename Func> |
85 | | constexpr void replace_with_new_short_string(size_t byte_count, Func&& callback) |
86 | 1.85M | { |
87 | 1.85M | Bytes buffer = replace_with_uninitialized_short_string(byte_count); |
88 | 1.85M | if (byte_count != 0) |
89 | 1.85M | callback(buffer); |
90 | 1.85M | } |
91 | | |
92 | | // This is not a trivial operation with storage, so it does not belong here. Unfortunately, it |
93 | | // is impossible to implement it without access to StringData. |
94 | | ErrorOr<StringBase> substring_from_byte_offset_with_shared_superstring(size_t start, size_t byte_count) const; |
95 | | |
96 | | private: |
97 | | friend class ::AK::String; |
98 | | friend class ::AK::FlyString; |
99 | | |
100 | | // NOTE: If the least significant bit of the pointer is set, this is a short string. |
101 | | static constexpr uintptr_t SHORT_STRING_FLAG = 1; |
102 | | |
103 | | explicit StringBase(NonnullRefPtr<Detail::StringData const>); |
104 | | |
105 | | explicit constexpr StringBase(ShortString short_string) |
106 | 36.4M | : m_short_string(short_string) |
107 | 36.4M | { |
108 | 36.4M | } |
109 | | |
110 | | ErrorOr<Bytes> replace_with_uninitialized_buffer(size_t byte_count); |
111 | | |
112 | | constexpr Bytes replace_with_uninitialized_short_string(size_t byte_count) |
113 | 27.4M | { |
114 | 27.4M | VERIFY(is_short_string()); |
115 | 27.4M | VERIFY(byte_count <= MAX_SHORT_STRING_BYTE_COUNT); |
116 | | |
117 | 27.4M | m_short_string = ShortString {}; |
118 | 27.4M | m_short_string.byte_count_and_short_string_flag = (byte_count << 1) | SHORT_STRING_FLAG; |
119 | 27.4M | return { m_short_string.storage, byte_count }; |
120 | 27.4M | } |
121 | | |
122 | | void destroy_string(); |
123 | | |
124 | | union { |
125 | | ShortString m_short_string; |
126 | | Detail::StringData const* m_data { nullptr }; |
127 | | }; |
128 | | }; |
129 | | |
130 | | } |