1 // Copyright 2014 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #pragma once
16
17 #include "android/base/Optional.h"
18 #include "android/base/TypeTraits.h"
19
20 #include <algorithm>
21 #include <cstring>
22 #include <string>
23
24 namespace android {
25 namespace base {
26
27 // A StringView is a simple (address, size) pair that points to an
28 // existing read-only string. It's a convenience class used to prevent
29 // the creation of std::string() objects un-necessarily.
30 //
31 // Consider the two following functions:
32 //
33 // size_t count1(const std::string& str) {
34 // size_t count = 0;
35 // for (size_t n = 0; n < str.size(); ++n) {
36 // if (str[n] == '1') {
37 // count++;
38 // }
39 // }
40 // return count;
41 // }
42 //
43 // size_t count2(const StringView& str) {
44 // size_t count = 0;
45 // for (size_t n = 0; n < str.size(); ++n) {
46 // if (str[n] == '2') {
47 // count++;
48 // }
49 // }
50 // return count;
51 // }
52 //
53 // Then consider the following calls:
54 //
55 // size_t n1 = count1("There is 1 one in this string!");
56 // size_t n2 = count2("I can count 2 too");
57 //
58 // In the first case, the compiler will silently create a temporary
59 // std::string object, copy the input string into it (allocating memory in
60 // the heap), call count1() and destroy the std::string upon its return.
61 //
62 // In the second case, the compiler will create a temporary StringView,
63 // initialize it trivially before calling count2(), this results in
64 // much less generated code, as well as better performance.
65 //
66 // Generally speaking, always use a reference or pointer to StringView
67 // instead of a std::string if your function or method doesn't need to modify
68 // its input.
69 //
70 class StringView {
71 public:
StringView()72 constexpr StringView() : mString(""), mSize(0U) {}
73
StringView(const StringView & other)74 constexpr StringView(const StringView& other) :
75 mString(other.data()), mSize(other.size()) {}
76
77 // IMPORTANT: all StringView constructors are intentionally not explict
78 // it is needed to allow seamless creation of StringView from all types
79 // of strings around - as it's intended to be a generic string wrapper
80
81 // A constexpr constructor from a constant buffer, initializing |mSize|
82 // as well. This allows one to declare a static const StringView instance
83 // and initialize it at compile time, with no runtime overhead:
84 //
85 // static constexpr StringView message = "blah";
86 //
87 template <size_t size>
StringView(const char (& buf)[size])88 constexpr StringView(const char (&buf)[size]) :
89 mString(buf), mSize(size - 1) {}
90
91 // Ctor for non-const arrays, AKA buffers. These usually contain some
92 // string formatted at runtime, so call strlen() instead of using the
93 // buffer size.
94 template <size_t size>
StringView(char (& buf)[size])95 constexpr StringView(char (&buf)[size]) :
96 mString(buf), mSize(strlen(buf)) {}
97
98 // Constructor from a const char pointer. It has to be templated to make
99 // sure the array-based one is chosen for an array - otherwise non-templated
100 // overload always wins
101 // Note: the parameter type is a const reference to a const pointer. This
102 // is to make this overload a poorer choice for the case of an array. For
103 // the 'const char[]' argument both 'reference to an array' and 'pointer'
104 // overloads are tied, so compiler can't choose without help
105 // Note2: for all constructors and set() calls, |end| must be
106 // dereferencable. It is notrequired to be '\0', but there has to be some
107 // data there. One may not construct a StringView passing past-the-end
108 // iterator as |end|! StringView will try to dereference it.
109 template <class Char, class = enable_if<std::is_same<Char, char>>>
StringView(const Char * const & string)110 constexpr StringView(const Char* const & string) :
111 mString(string ? string : ""), mSize(string ? strlen(string) : 0) {}
112
StringView(const std::string & str)113 StringView(const std::string& str) :
114 mString(str.c_str()), mSize(str.size()) {}
115
StringView(const char * str,size_t len)116 constexpr StringView(const char* str, size_t len)
117 : mString(str ? str : ""), mSize(len) {}
118
StringView(const char * begin,const char * end)119 constexpr StringView(const char* begin, const char* end)
120 : mString(begin ? begin : ""), mSize(begin ? end - begin : 0) {}
121
StringView(std::nullptr_t)122 constexpr StringView(std::nullptr_t) :
123 mString(""), mSize(0) {}
124
str()125 std::string str() const { return std::string(mString, mString + mSize); }
data()126 constexpr const char* data() const { return mString; }
size()127 constexpr size_t size() const { return mSize; }
128
129 typedef const char* iterator;
130 typedef const char* const_iterator;
131
begin()132 constexpr const_iterator begin() const { return mString; }
end()133 constexpr const_iterator end() const { return mString + mSize; }
134
empty()135 constexpr bool empty() const { return !size(); }
isNullTerminated()136 constexpr bool isNullTerminated() const { return *end() == '\0'; }
137
clear()138 void clear() {
139 mSize = 0;
140 mString = "";
141 }
142
143 constexpr char operator[](size_t index) const {
144 return mString[index];
145 }
146
set(const char * data,size_t len)147 void set(const char* data, size_t len) {
148 mString = data ? data : "";
149 mSize = len;
150 }
151
set(const char * str)152 void set(const char* str) {
153 mString = str ? str : "";
154 mSize = ::strlen(mString);
155 }
156
set(const StringView & other)157 void set(const StringView& other) {
158 mString = other.mString;
159 mSize = other.mSize;
160 }
161
162 // Compare with another StringView.
163 int compare(const StringView& other) const;
164
165 StringView& operator=(const StringView& other) {
166 set(other);
167 return *this;
168 }
169
170 // find() first occurrence of |other| with an initial offset.
171 // Returns absolute offset (does not include |off|).
172 size_t find(StringView other, size_t off = 0) {
173 // Trivial case
174 if (!other.mSize) return 0;
175
176 size_t safeOff = std::min(off, mSize);
177
178 const char* searchStart = mString + safeOff;
179 const char* searchEnd = searchStart + mSize - safeOff;
180
181 const char* res =
182 std::search(searchStart, searchEnd,
183 other.mString, other.mString + other.mSize);
184 if (res == searchEnd) return std::string::npos;
185 return (size_t)((uintptr_t)res - (uintptr_t)mString);
186 }
187
188 // getSubstr(); returns this string starting at the first place |other|
189 // occurs, otherwise a blank string.
190 StringView getSubstr(StringView other, size_t off = 0) {
191 size_t loc = find(other, off);
192 if (loc == std::string::npos) return StringView("");
193 return { mString + loc, end() };
194 }
195
196 // Returns substring starting at |begin| and running for |len|,
197 // or the rest of the string if |len| is std::string::npos.
198 StringView substr(size_t begin, size_t len = std::string::npos) {
199 if (len == std::string::npos) {
200 len = mSize - begin;
201 }
202 size_t safeOff = std::min(begin, mSize);
203 size_t safeLen = std::min(len, mSize - safeOff);
204 return { mString + safeOff, safeLen };
205 }
206
207 // Returns substring starting at |begin| ending at |end|,
208 // or the rest of the string if |end is std::string::npos.
209 StringView substrAbs(size_t begin, size_t end = std::string::npos) {
210 if (end == std::string::npos) {
211 end = begin + mSize;
212 }
213 return substr(begin, end - begin);
214 }
215
216 // Convert to std::string when needed.
string()217 operator std::string() const { return std::string(mString, mSize); }
218
219 private:
220 const char* mString;
221 size_t mSize;
222 };
223
224 // Comparison operators. Defined as functions to allow automatic type
225 // conversions with C strings and std::string objects.
226
227 bool operator==(const StringView& x, const StringView& y);
228
229 inline bool operator!=(const StringView& x, const StringView& y) {
230 return !(x == y);
231 }
232
233 inline bool operator<(const StringView& x, const StringView& y) {
234 return x.compare(y) < 0;
235 }
236
237 inline bool operator>=(const StringView& x, const StringView& y) {
238 return !(x < y);
239 }
240
241 inline bool operator >(const StringView& x, const StringView& y) {
242 return x.compare(y) > 0;
243 }
244
245 inline bool operator<=(const StringView& x, const StringView& y) {
246 return !(x > y);
247 }
248
249 // Helper to get a null-terminated const char* from a string view.
250 // Only allocates if the StringView is not null terminated.
251 //
252 // Usage:
253 //
254 // StringView myString = ...;
255 // printf("Contents: %s\n", c_str(myString));
256 //
257 // c_str(...) constructs a temporary object that may allocate memory if the
258 // StringView is not null termianted. The lifetime of the temporary object will
259 // be until the next sequence point (typically the next semicolon). If the
260 // value needs to exist for longer than that, cache the instance.
261 //
262 // StringView myString = ...;
263 // auto myNullTerminatedString = c_str(myString);
264 // functionAcceptingConstCharPointer(myNullTerminatedString);
265 //
266 class CStrWrapper {
267 public:
CStrWrapper(StringView stringView)268 CStrWrapper(StringView stringView) : mStringView(stringView) {}
269
270 // Returns a null-terminated char*, potentially creating a copy to add a
271 // null terminator.
get()272 const char* get() {
273 if (mStringView.isNullTerminated()) {
274 return mStringView.data();
275 } else {
276 // Create the std::string copy on-demand.
277 if (!mStringCopy) {
278 mStringCopy.emplace(mStringView.str());
279 }
280
281 return mStringCopy->c_str();
282 }
283 }
284
285 // Enable casting to const char*
286 operator const char*() { return get(); }
287
288 private:
289 const StringView mStringView;
290 Optional<std::string> mStringCopy;
291 };
292
c_str(StringView stringView)293 inline CStrWrapper c_str(StringView stringView) {
294 return CStrWrapper(stringView);
295 }
296
297 } // namespace base
298 } // namespace android
299