// Copyright 2014 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #pragma once #include "android/base/Optional.h" #include "android/base/TypeTraits.h" #include #include #include namespace android { namespace base { // A StringView is a simple (address, size) pair that points to an // existing read-only string. It's a convenience class used to prevent // the creation of std::string() objects un-necessarily. // // Consider the two following functions: // // size_t count1(const std::string& str) { // size_t count = 0; // for (size_t n = 0; n < str.size(); ++n) { // if (str[n] == '1') { // count++; // } // } // return count; // } // // size_t count2(const StringView& str) { // size_t count = 0; // for (size_t n = 0; n < str.size(); ++n) { // if (str[n] == '2') { // count++; // } // } // return count; // } // // Then consider the following calls: // // size_t n1 = count1("There is 1 one in this string!"); // size_t n2 = count2("I can count 2 too"); // // In the first case, the compiler will silently create a temporary // std::string object, copy the input string into it (allocating memory in // the heap), call count1() and destroy the std::string upon its return. // // In the second case, the compiler will create a temporary StringView, // initialize it trivially before calling count2(), this results in // much less generated code, as well as better performance. // // Generally speaking, always use a reference or pointer to StringView // instead of a std::string if your function or method doesn't need to modify // its input. // class StringView { public: constexpr StringView() : mString(""), mSize(0U) {} constexpr StringView(const StringView& other) : mString(other.data()), mSize(other.size()) {} // IMPORTANT: all StringView constructors are intentionally not explict // it is needed to allow seamless creation of StringView from all types // of strings around - as it's intended to be a generic string wrapper // A constexpr constructor from a constant buffer, initializing |mSize| // as well. This allows one to declare a static const StringView instance // and initialize it at compile time, with no runtime overhead: // // static constexpr StringView message = "blah"; // template constexpr StringView(const char (&buf)[size]) : mString(buf), mSize(size - 1) {} // Ctor for non-const arrays, AKA buffers. These usually contain some // string formatted at runtime, so call strlen() instead of using the // buffer size. template constexpr StringView(char (&buf)[size]) : mString(buf), mSize(strlen(buf)) {} // Constructor from a const char pointer. It has to be templated to make // sure the array-based one is chosen for an array - otherwise non-templated // overload always wins // Note: the parameter type is a const reference to a const pointer. This // is to make this overload a poorer choice for the case of an array. For // the 'const char[]' argument both 'reference to an array' and 'pointer' // overloads are tied, so compiler can't choose without help // Note2: for all constructors and set() calls, |end| must be // dereferencable. It is notrequired to be '\0', but there has to be some // data there. One may not construct a StringView passing past-the-end // iterator as |end|! StringView will try to dereference it. template >> constexpr StringView(const Char* const & string) : mString(string ? string : ""), mSize(string ? strlen(string) : 0) {} StringView(const std::string& str) : mString(str.c_str()), mSize(str.size()) {} constexpr StringView(const char* str, size_t len) : mString(str ? str : ""), mSize(len) {} constexpr StringView(const char* begin, const char* end) : mString(begin ? begin : ""), mSize(begin ? end - begin : 0) {} constexpr StringView(std::nullptr_t) : mString(""), mSize(0) {} std::string str() const { return std::string(mString, mString + mSize); } constexpr const char* data() const { return mString; } constexpr size_t size() const { return mSize; } typedef const char* iterator; typedef const char* const_iterator; constexpr const_iterator begin() const { return mString; } constexpr const_iterator end() const { return mString + mSize; } constexpr bool empty() const { return !size(); } constexpr bool isNullTerminated() const { return *end() == '\0'; } void clear() { mSize = 0; mString = ""; } constexpr char operator[](size_t index) const { return mString[index]; } void set(const char* data, size_t len) { mString = data ? data : ""; mSize = len; } void set(const char* str) { mString = str ? str : ""; mSize = ::strlen(mString); } void set(const StringView& other) { mString = other.mString; mSize = other.mSize; } // Compare with another StringView. int compare(const StringView& other) const; StringView& operator=(const StringView& other) { set(other); return *this; } // find() first occurrence of |other| with an initial offset. // Returns absolute offset (does not include |off|). size_t find(StringView other, size_t off = 0) { // Trivial case if (!other.mSize) return 0; size_t safeOff = std::min(off, mSize); const char* searchStart = mString + safeOff; const char* searchEnd = searchStart + mSize - safeOff; const char* res = std::search(searchStart, searchEnd, other.mString, other.mString + other.mSize); if (res == searchEnd) return std::string::npos; return (size_t)((uintptr_t)res - (uintptr_t)mString); } // getSubstr(); returns this string starting at the first place |other| // occurs, otherwise a blank string. StringView getSubstr(StringView other, size_t off = 0) { size_t loc = find(other, off); if (loc == std::string::npos) return StringView(""); return { mString + loc, end() }; } // Returns substring starting at |begin| and running for |len|, // or the rest of the string if |len| is std::string::npos. StringView substr(size_t begin, size_t len = std::string::npos) { if (len == std::string::npos) { len = mSize - begin; } size_t safeOff = std::min(begin, mSize); size_t safeLen = std::min(len, mSize - safeOff); return { mString + safeOff, safeLen }; } // Returns substring starting at |begin| ending at |end|, // or the rest of the string if |end is std::string::npos. StringView substrAbs(size_t begin, size_t end = std::string::npos) { if (end == std::string::npos) { end = begin + mSize; } return substr(begin, end - begin); } // Convert to std::string when needed. operator std::string() const { return std::string(mString, mSize); } private: const char* mString; size_t mSize; }; // Comparison operators. Defined as functions to allow automatic type // conversions with C strings and std::string objects. bool operator==(const StringView& x, const StringView& y); inline bool operator!=(const StringView& x, const StringView& y) { return !(x == y); } inline bool operator<(const StringView& x, const StringView& y) { return x.compare(y) < 0; } inline bool operator>=(const StringView& x, const StringView& y) { return !(x < y); } inline bool operator >(const StringView& x, const StringView& y) { return x.compare(y) > 0; } inline bool operator<=(const StringView& x, const StringView& y) { return !(x > y); } // Helper to get a null-terminated const char* from a string view. // Only allocates if the StringView is not null terminated. // // Usage: // // StringView myString = ...; // printf("Contents: %s\n", c_str(myString)); // // c_str(...) constructs a temporary object that may allocate memory if the // StringView is not null termianted. The lifetime of the temporary object will // be until the next sequence point (typically the next semicolon). If the // value needs to exist for longer than that, cache the instance. // // StringView myString = ...; // auto myNullTerminatedString = c_str(myString); // functionAcceptingConstCharPointer(myNullTerminatedString); // class CStrWrapper { public: CStrWrapper(StringView stringView) : mStringView(stringView) {} // Returns a null-terminated char*, potentially creating a copy to add a // null terminator. const char* get() { if (mStringView.isNullTerminated()) { return mStringView.data(); } else { // Create the std::string copy on-demand. if (!mStringCopy) { mStringCopy.emplace(mStringView.str()); } return mStringCopy->c_str(); } } // Enable casting to const char* operator const char*() { return get(); } private: const StringView mStringView; Optional mStringCopy; }; inline CStrWrapper c_str(StringView stringView) { return CStrWrapper(stringView); } } // namespace base } // namespace android