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