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#pragma once
28
29#include <AK/Forward.h>
30#include <AK/RefPtr.h>
31#include <AK/StringImpl.h>
32#include <AK/StringUtils.h>
33#include <AK/Traits.h>
34
35namespace AK {
36
37// String is a convenience wrapper around StringImpl, suitable for passing
38// around as a value type. It's basically the same as passing around a
39// RefPtr<StringImpl>, with a bit of syntactic sugar.
40//
41// Note that StringImpl is an immutable object that cannot shrink or grow.
42// Its allocation size is snugly tailored to the specific string it contains.
43// Copying a String is very efficient, since the internal StringImpl is
44// retainable and so copying only requires modifying the ref count.
45//
46// There are three main ways to construct a new String:
47//
48// s = String("some literal");
49//
50// s = String::format("%d little piggies", m_piggies);
51//
52// StringBuilder builder;
53// builder.append("abc");
54// builder.append("123");
55// s = builder.to_string();
56
57class String {
58public:
59 using ConstIterator = const char*;
60
61 ~String() {}
62
63 String() {}
64 String(const StringView&);
65
66 String(const String& other)
67 : m_impl(const_cast<String&>(other).m_impl)
68 {
69 }
70
71 String(String&& other)
72 : m_impl(move(other.m_impl))
73 {
74 }
75
76 String(const char* cstring, ShouldChomp shouldChomp = NoChomp)
77 : m_impl(StringImpl::create(cstring, shouldChomp))
78 {
79 }
80
81 String(const char* cstring, size_t length, ShouldChomp shouldChomp = NoChomp)
82 : m_impl(StringImpl::create(cstring, length, shouldChomp))
83 {
84 }
85
86 String(const StringImpl& impl)
87 : m_impl(const_cast<StringImpl&>(impl))
88 {
89 }
90
91 String(const StringImpl* impl)
92 : m_impl(const_cast<StringImpl*>(impl))
93 {
94 }
95
96 String(RefPtr<StringImpl>&& impl)
97 : m_impl(move(impl))
98 {
99 }
100
101 String(NonnullRefPtr<StringImpl>&& impl)
102 : m_impl(move(impl))
103 {
104 }
105
106 String(const FlyString&);
107
108 static String repeated(char, size_t count);
109 bool matches(const StringView& mask, CaseSensitivity = CaseSensitivity::CaseInsensitive) const;
110
111 int to_int(bool& ok) const;
112 unsigned to_uint(bool& ok) const;
113
114 String to_lowercase() const;
115 String to_uppercase() const;
116
117 bool equals_ignoring_case(const StringView&) const;
118
119 bool contains(const String&) const;
120
121 Vector<String> split_limit(char separator, size_t limit, bool keep_empty = false) const;
122 Vector<String> split(char separator, bool keep_empty = false) const;
123 String substring(size_t start, size_t length) const;
124
125 Vector<StringView> split_view(char separator, bool keep_empty = false) const;
126 StringView substring_view(size_t start, size_t length) const;
127
128 bool is_null() const { return !m_impl; }
129 bool is_empty() const { return length() == 0; }
130 size_t length() const { return m_impl ? m_impl->length() : 0; }
131 const char* characters() const { return m_impl ? m_impl->characters() : nullptr; }
132 const char& operator[](size_t i) const
133 {
134 return (*m_impl)[i];
135 }
136
137 ConstIterator begin() const { return characters(); }
138 ConstIterator end() const { return begin() + length(); }
139
140 bool starts_with(const StringView&) const;
141 bool ends_with(const StringView&) const;
142 bool starts_with(char) const;
143 bool ends_with(char) const;
144
145 bool operator==(const String&) const;
146 bool operator!=(const String& other) const { return !(*this == other); }
147
148 bool operator==(const StringView&) const;
149 bool operator!=(const StringView& other) const { return !(*this == other); }
150
151 bool operator==(const FlyString&) const;
152 bool operator!=(const FlyString& other) const { return !(*this == other); }
153
154 bool operator<(const String&) const;
155 bool operator<(const char*) const;
156 bool operator>=(const String& other) const { return !(*this < other); }
157 bool operator>=(const char* other) const { return !(*this < other); }
158
159 bool operator>(const String&) const;
160 bool operator>(const char*) const;
161 bool operator<=(const String& other) const { return !(*this > other); }
162 bool operator<=(const char* other) const { return !(*this > other); }
163
164 bool operator==(const char* cstring) const;
165 bool operator!=(const char* cstring) const { return !(*this == cstring); }
166
167 String isolated_copy() const;
168
169 static String empty();
170
171 StringImpl* impl() { return m_impl.ptr(); }
172 const StringImpl* impl() const { return m_impl.ptr(); }
173
174 String& operator=(String&& other)
175 {
176 if (this != &other)
177 m_impl = move(other.m_impl);
178 return *this;
179 }
180
181 String& operator=(const String& other)
182 {
183 if (this != &other)
184 m_impl = const_cast<String&>(other).m_impl;
185 return *this;
186 }
187
188 u32 hash() const
189 {
190 if (!m_impl)
191 return 0;
192 return m_impl->hash();
193 }
194
195 ByteBuffer to_byte_buffer() const;
196
197 template<typename BufferType>
198 static String copy(const BufferType& buffer, ShouldChomp should_chomp = NoChomp)
199 {
200 if (buffer.is_null())
201 return {};
202 if (buffer.is_empty())
203 return empty();
204 return String((const char*)buffer.data(), buffer.size(), should_chomp);
205 }
206
207 static String format(const char*, ...);
208 static String number(unsigned);
209 static String number(unsigned long);
210 static String number(unsigned long long);
211 static String number(int);
212 static String number(long);
213 static String number(long long);
214
215 StringView view() const;
216
217 int replace(const String& needle, const String& replacement, bool all_occurences = false);
218
219private:
220 RefPtr<StringImpl> m_impl;
221};
222
223template<>
224struct Traits<String> : public GenericTraits<String> {
225 static unsigned hash(const String& s) { return s.impl() ? s.impl()->hash() : 0; }
226};
227
228struct CaseInsensitiveStringTraits : public AK::Traits<String> {
229 static unsigned hash(const String& s) { return s.impl() ? s.to_lowercase().impl()->hash() : 0; }
230 static bool equals(const String& a, const String& b) { return a.to_lowercase() == b.to_lowercase(); }
231};
232
233bool operator<(const char*, const String&);
234bool operator>=(const char*, const String&);
235bool operator>(const char*, const String&);
236bool operator<=(const char*, const String&);
237
238String escape_html_entities(const StringView& html);
239
240}
241
242using AK::CaseInsensitiveStringTraits;
243using AK::escape_html_entities;
244using AK::String;