Coverage Report

Created: 2026-02-16 07:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}