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