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 "StringImpl.h"
28#include "HashTable.h"
29#include "StdLibExtras.h"
30#include "kmalloc.h"
31
32//#define DEBUG_STRINGIMPL
33
34#ifdef DEBUG_STRINGIMPL
35unsigned g_stringimpl_count;
36static HashTable<StringImpl*>* g_all_live_stringimpls;
37
38void dump_all_stringimpls()
39{
40 unsigned i = 0;
41 for (auto& it : *g_all_live_stringimpls) {
42 dbgprsize_tf("%u: \"%s\"\n", i, (*it).characters());
43 ++i;
44 }
45}
46#endif
47
48namespace AK {
49
50static StringImpl* s_the_empty_stringimpl = nullptr;
51
52StringImpl& StringImpl::the_empty_stringimpl()
53{
54 if (!s_the_empty_stringimpl) {
55 void* slot = kmalloc(sizeof(StringImpl) + sizeof(char));
56 s_the_empty_stringimpl = new (slot) StringImpl(ConstructTheEmptyStringImpl);
57 }
58 return *s_the_empty_stringimpl;
59}
60
61StringImpl::StringImpl(ConstructWithInlineBufferTag, size_t length)
62 : m_length(length)
63{
64#ifdef DEBUG_STRINGIMPL
65 if (!g_all_live_stringimpls)
66 g_all_live_stringimpls = new HashTable<StringImpl*>;
67 ++g_stringimpl_count;
68 g_all_live_stringimpls->set(this);
69#endif
70}
71
72StringImpl::~StringImpl()
73{
74#ifdef DEBUG_STRINGIMPL
75 --g_stringimpl_count;
76 g_all_live_stringimpls->remove(this);
77#endif
78}
79
80static inline size_t allocation_size_for_stringimpl(size_t length)
81{
82 return sizeof(StringImpl) + (sizeof(char) * length) + sizeof(char);
83}
84
85NonnullRefPtr<StringImpl> StringImpl::create_uninitialized(size_t length, char*& buffer)
86{
87 ASSERT(length);
88 void* slot = kmalloc(allocation_size_for_stringimpl(length));
89 ASSERT(slot);
90 auto new_stringimpl = adopt(*new (slot) StringImpl(ConstructWithInlineBuffer, length));
91 buffer = const_cast<char*>(new_stringimpl->characters());
92 buffer[length] = '\0';
93 return new_stringimpl;
94}
95
96RefPtr<StringImpl> StringImpl::create(const char* cstring, size_t length, ShouldChomp should_chomp)
97{
98 if (!cstring)
99 return nullptr;
100
101 if (!length || !*cstring)
102 return the_empty_stringimpl();
103
104 if (should_chomp) {
105 while (length) {
106 char last_ch = cstring[length - 1];
107 if (!last_ch || last_ch == '\n' || last_ch == '\r')
108 --length;
109 else
110 break;
111 }
112 }
113
114 if (!length)
115 return the_empty_stringimpl();
116
117 char* buffer;
118 auto new_stringimpl = create_uninitialized(length, buffer);
119 memcpy(buffer, cstring, length * sizeof(char));
120
121 return new_stringimpl;
122}
123
124RefPtr<StringImpl> StringImpl::create(const char* cstring, ShouldChomp shouldChomp)
125{
126 if (!cstring)
127 return nullptr;
128
129 return create(cstring, strlen(cstring), shouldChomp);
130}
131
132static inline bool is_ascii_lowercase(char c)
133{
134 return c >= 'a' && c <= 'z';
135}
136
137static inline bool is_ascii_uppercase(char c)
138{
139 return c >= 'A' && c <= 'Z';
140}
141
142static inline char to_ascii_lowercase(char c)
143{
144 if (is_ascii_uppercase(c))
145 return c | 0x20;
146 return c;
147}
148
149static inline char to_ascii_uppercase(char c)
150{
151 if (is_ascii_lowercase(c))
152 return c & ~0x20;
153 return c;
154}
155
156NonnullRefPtr<StringImpl> StringImpl::to_lowercase() const
157{
158 for (size_t i = 0; i < m_length; ++i) {
159 if (!is_ascii_lowercase(characters()[i]))
160 goto slow_path;
161 }
162 return const_cast<StringImpl&>(*this);
163
164slow_path:
165 char* buffer;
166 auto lowercased = create_uninitialized(m_length, buffer);
167 for (size_t i = 0; i < m_length; ++i)
168 buffer[i] = to_ascii_lowercase(characters()[i]);
169 return lowercased;
170}
171
172NonnullRefPtr<StringImpl> StringImpl::to_uppercase() const
173{
174 for (size_t i = 0; i < m_length; ++i) {
175 if (!is_ascii_uppercase(characters()[i]))
176 goto slow_path;
177 }
178 return const_cast<StringImpl&>(*this);
179
180slow_path:
181 char* buffer;
182 auto uppercased = create_uninitialized(m_length, buffer);
183 for (size_t i = 0; i < m_length; ++i)
184 buffer[i] = to_ascii_uppercase(characters()[i]);
185 return uppercased;
186}
187
188void StringImpl::compute_hash() const
189{
190 if (!length())
191 m_hash = 0;
192 else
193 m_hash = string_hash(characters(), m_length);
194 m_has_hash = true;
195}
196
197}