Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <AK/FlyString.h>
28#include <AK/HashTable.h>
29#include <AK/Memory.h>
30#include <AK/StdLibExtras.h>
31#include <AK/StringImpl.h>
32#include <AK/kmalloc.h>
33
34//#define DEBUG_STRINGIMPL
35
36#ifdef DEBUG_STRINGIMPL
37unsigned g_stringimpl_count;
38static HashTable<StringImpl*>* g_all_live_stringimpls;
39
40void dump_all_stringimpls()
41{
42 unsigned i = 0;
43 for (auto& it : *g_all_live_stringimpls) {
44 dbgprsize_tf("%u: \"%s\"\n", i, (*it).characters());
45 ++i;
46 }
47}
48#endif
49
50namespace AK {
51
52static StringImpl* s_the_empty_stringimpl = nullptr;
53
54StringImpl& StringImpl::the_empty_stringimpl()
55{
56 if (!s_the_empty_stringimpl) {
57 void* slot = kmalloc(sizeof(StringImpl) + sizeof(char));
58 s_the_empty_stringimpl = new (slot) StringImpl(ConstructTheEmptyStringImpl);
59 }
60 return *s_the_empty_stringimpl;
61}
62
63StringImpl::StringImpl(ConstructWithInlineBufferTag, size_t length)
64 : m_length(length)
65{
66#ifdef DEBUG_STRINGIMPL
67 if (!g_all_live_stringimpls)
68 g_all_live_stringimpls = new HashTable<StringImpl*>;
69 ++g_stringimpl_count;
70 g_all_live_stringimpls->set(this);
71#endif
72}
73
74StringImpl::~StringImpl()
75{
76 if (m_fly)
77 FlyString::did_destroy_impl({}, *this);
78#ifdef DEBUG_STRINGIMPL
79 --g_stringimpl_count;
80 g_all_live_stringimpls->remove(this);
81#endif
82}
83
84static inline size_t allocation_size_for_stringimpl(size_t length)
85{
86 return sizeof(StringImpl) + (sizeof(char) * length) + sizeof(char);
87}
88
89NonnullRefPtr<StringImpl> StringImpl::create_uninitialized(size_t length, char*& buffer)
90{
91 ASSERT(length);
92 void* slot = kmalloc(allocation_size_for_stringimpl(length));
93 ASSERT(slot);
94 auto new_stringimpl = adopt(*new (slot) StringImpl(ConstructWithInlineBuffer, length));
95 buffer = const_cast<char*>(new_stringimpl->characters());
96 buffer[length] = '\0';
97 return new_stringimpl;
98}
99
100RefPtr<StringImpl> StringImpl::create(const char* cstring, size_t length, ShouldChomp should_chomp)
101{
102 if (!cstring)
103 return nullptr;
104
105 if (!length || !*cstring)
106 return the_empty_stringimpl();
107
108 if (should_chomp) {
109 while (length) {
110 char last_ch = cstring[length - 1];
111 if (!last_ch || last_ch == '\n' || last_ch == '\r')
112 --length;
113 else
114 break;
115 }
116 }
117
118 if (!length)
119 return the_empty_stringimpl();
120
121 char* buffer;
122 auto new_stringimpl = create_uninitialized(length, buffer);
123 memcpy(buffer, cstring, length * sizeof(char));
124
125 return new_stringimpl;
126}
127
128RefPtr<StringImpl> StringImpl::create(const char* cstring, ShouldChomp shouldChomp)
129{
130 if (!cstring)
131 return nullptr;
132
133 return create(cstring, strlen(cstring), shouldChomp);
134}
135
136static inline bool is_ascii_lowercase(char c)
137{
138 return c >= 'a' && c <= 'z';
139}
140
141static inline bool is_ascii_uppercase(char c)
142{
143 return c >= 'A' && c <= 'Z';
144}
145
146static inline char to_ascii_lowercase(char c)
147{
148 if (is_ascii_uppercase(c))
149 return c | 0x20;
150 return c;
151}
152
153static inline char to_ascii_uppercase(char c)
154{
155 if (is_ascii_lowercase(c))
156 return c & ~0x20;
157 return c;
158}
159
160NonnullRefPtr<StringImpl> StringImpl::to_lowercase() const
161{
162 for (size_t i = 0; i < m_length; ++i) {
163 if (!is_ascii_lowercase(characters()[i]))
164 goto slow_path;
165 }
166 return const_cast<StringImpl&>(*this);
167
168slow_path:
169 char* buffer;
170 auto lowercased = create_uninitialized(m_length, buffer);
171 for (size_t i = 0; i < m_length; ++i)
172 buffer[i] = to_ascii_lowercase(characters()[i]);
173 return lowercased;
174}
175
176NonnullRefPtr<StringImpl> StringImpl::to_uppercase() const
177{
178 for (size_t i = 0; i < m_length; ++i) {
179 if (!is_ascii_uppercase(characters()[i]))
180 goto slow_path;
181 }
182 return const_cast<StringImpl&>(*this);
183
184slow_path:
185 char* buffer;
186 auto uppercased = create_uninitialized(m_length, buffer);
187 for (size_t i = 0; i < m_length; ++i)
188 buffer[i] = to_ascii_uppercase(characters()[i]);
189 return uppercased;
190}
191
192void StringImpl::compute_hash() const
193{
194 if (!length())
195 m_hash = 0;
196 else
197 m_hash = string_hash(characters(), m_length);
198 m_has_hash = true;
199}
200
201}