Coverage Report

Created: 2026-11-13 08:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/AK/StringData.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/RefCounted.h>
10
#include <AK/kmalloc.h>
11
12
namespace AK::Detail {
13
14
class StringData final : public RefCounted<StringData> {
15
public:
16
    static ErrorOr<NonnullRefPtr<StringData>> create_uninitialized(size_t byte_count, u8*& buffer)
17
5.47M
    {
18
5.47M
        VERIFY(byte_count);
19
5.47M
        void* slot = malloc(allocation_size_for_string_data(byte_count));
20
5.47M
        if (!slot) {
21
0
            return Error::from_errno(ENOMEM);
22
0
        }
23
5.47M
        auto new_string_data = adopt_ref(*new (slot) StringData(byte_count));
24
5.47M
        buffer = const_cast<u8*>(new_string_data->bytes().data());
25
5.47M
        return new_string_data;
26
5.47M
    }
27
28
    static ErrorOr<NonnullRefPtr<StringData>> create_substring(StringData const& superstring, size_t start, size_t byte_count)
29
2.65k
    {
30
        // Strings of MAX_SHORT_STRING_BYTE_COUNT bytes or less should be handled by the String short string optimization.
31
2.65k
        VERIFY(byte_count > MAX_SHORT_STRING_BYTE_COUNT);
32
33
2.65k
        void* slot = malloc(sizeof(StringData) + sizeof(StringData::SubstringData));
34
2.65k
        if (!slot) {
35
0
            return Error::from_errno(ENOMEM);
36
0
        }
37
2.65k
        return adopt_ref(*new (slot) StringData(superstring, start, byte_count));
38
2.65k
    }
39
40
    struct SubstringData {
41
        StringData const* superstring { nullptr };
42
        u32 start_offset { 0 };
43
    };
44
45
    void operator delete(void* ptr)
46
5.47M
    {
47
5.47M
        kfree_sized(ptr, allocation_size_for_string_data(static_cast<StringData const*>(ptr)->m_byte_count));
48
5.47M
    }
49
50
    ~StringData()
51
5.47M
    {
52
5.47M
        if (m_substring)
53
2.65k
            substring_data().superstring->unref();
54
5.47M
        if (m_is_fly_string)
55
841k
            FlyString::did_destroy_fly_string_data({}, *this);
56
5.47M
    }
57
58
    SubstringData const& substring_data() const
59
6.91k
    {
60
6.91k
        return *reinterpret_cast<SubstringData const*>(m_bytes_or_substring_data);
61
6.91k
    }
62
63
    // NOTE: There is no guarantee about null-termination.
64
    ReadonlyBytes bytes() const
65
21.2M
    {
66
21.2M
        if (m_substring) {
67
1.60k
            auto const& data = substring_data();
68
1.60k
            return data.superstring->bytes().slice(data.start_offset, m_byte_count);
69
1.60k
        }
70
21.2M
        return { &m_bytes_or_substring_data[0], m_byte_count };
71
21.2M
    }
72
73
1.75M
    StringView bytes_as_string_view() const { return { bytes() }; }
74
75
    bool operator==(StringData const& other) const
76
876k
    {
77
876k
        return bytes_as_string_view() == other.bytes_as_string_view();
78
876k
    }
79
80
    unsigned hash() const
81
1.71M
    {
82
1.71M
        if (!m_has_hash)
83
861k
            compute_hash();
84
1.71M
        return m_hash;
85
1.71M
    }
86
87
864k
    bool is_fly_string() const { return m_is_fly_string; }
88
841k
    void set_fly_string(bool is_fly_string) const { m_is_fly_string = is_fly_string; }
89
90
5.83k
    size_t byte_count() const { return m_byte_count; }
91
92
private:
93
    static constexpr size_t allocation_size_for_string_data(size_t length)
94
10.9M
    {
95
10.9M
        return sizeof(StringData) + (sizeof(char) * length);
96
10.9M
    }
97
98
    explicit StringData(size_t byte_count)
99
5.47M
        : m_byte_count(byte_count)
100
5.47M
    {
101
5.47M
    }
102
103
    StringData(StringData const& superstring, size_t start, size_t byte_count)
104
2.65k
        : m_byte_count(byte_count)
105
2.65k
        , m_substring(true)
106
2.65k
    {
107
2.65k
        auto& data = const_cast<SubstringData&>(substring_data());
108
2.65k
        data.start_offset = start;
109
2.65k
        data.superstring = &superstring;
110
2.65k
        superstring.ref();
111
2.65k
    }
112
113
    void compute_hash() const
114
861k
    {
115
861k
        auto bytes = this->bytes();
116
861k
        if (bytes.size() == 0)
117
0
            m_hash = 0;
118
861k
        else
119
861k
            m_hash = string_hash(reinterpret_cast<char const*>(bytes.data()), bytes.size());
120
861k
        m_has_hash = true;
121
861k
    }
122
123
    u32 m_byte_count { 0 };
124
    mutable unsigned m_hash { 0 };
125
    mutable bool m_has_hash { false };
126
    bool m_substring { false };
127
    mutable bool m_is_fly_string { false };
128
129
    alignas(SubstringData) u8 m_bytes_or_substring_data[0];
130
};
131
132
}