annotate CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/include/kj/string.h @ 69:33d812a61356

planemo upload commit 2e9511a184a1ca667c7be0c6321a36dc4e3d116d
author jpayne
date Tue, 18 Mar 2025 17:55:14 -0400
parents
children
rev   line source
jpayne@69 1 // Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
jpayne@69 2 // Licensed under the MIT License:
jpayne@69 3 //
jpayne@69 4 // Permission is hereby granted, free of charge, to any person obtaining a copy
jpayne@69 5 // of this software and associated documentation files (the "Software"), to deal
jpayne@69 6 // in the Software without restriction, including without limitation the rights
jpayne@69 7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
jpayne@69 8 // copies of the Software, and to permit persons to whom the Software is
jpayne@69 9 // furnished to do so, subject to the following conditions:
jpayne@69 10 //
jpayne@69 11 // The above copyright notice and this permission notice shall be included in
jpayne@69 12 // all copies or substantial portions of the Software.
jpayne@69 13 //
jpayne@69 14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
jpayne@69 15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
jpayne@69 16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
jpayne@69 17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
jpayne@69 18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
jpayne@69 19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
jpayne@69 20 // THE SOFTWARE.
jpayne@69 21
jpayne@69 22 #pragma once
jpayne@69 23
jpayne@69 24 #include <initializer_list>
jpayne@69 25 #include "array.h"
jpayne@69 26 #include "kj/common.h"
jpayne@69 27 #include <string.h>
jpayne@69 28
jpayne@69 29 KJ_BEGIN_HEADER
jpayne@69 30
jpayne@69 31 namespace kj {
jpayne@69 32 class StringPtr;
jpayne@69 33 class LiteralStringConst;
jpayne@69 34 class String;
jpayne@69 35 class ConstString;
jpayne@69 36
jpayne@69 37 class StringTree; // string-tree.h
jpayne@69 38 }
jpayne@69 39
jpayne@69 40 constexpr kj::StringPtr operator "" _kj(const char* str, size_t n);
jpayne@69 41 // You can append _kj to a string literal to make its type be StringPtr. There are a few cases
jpayne@69 42 // where you must do this for correctness:
jpayne@69 43 // - When you want to declare a constexpr StringPtr. Without _kj, this is a compile error.
jpayne@69 44 // - When you want to initialize a static/global StringPtr from a string literal without forcing
jpayne@69 45 // global constructor code to run at dynamic initialization time.
jpayne@69 46 // - When you have a string literal that contains NUL characters. Without _kj, the string will
jpayne@69 47 // be considered to end at the first NUL.
jpayne@69 48 // - When you want to initialize an ArrayPtr<const char> from a string literal, without including
jpayne@69 49 // the NUL terminator in the data. (Initializing an ArrayPtr from a regular string literal is
jpayne@69 50 // a compile error specifically due to this ambiguity.)
jpayne@69 51 //
jpayne@69 52 // In other cases, there should be no difference between initializing a StringPtr from a regular
jpayne@69 53 // string literal vs. one with _kj (assuming the compiler is able to optimize away strlen() on a
jpayne@69 54 // string literal).
jpayne@69 55
jpayne@69 56 constexpr kj::LiteralStringConst operator "" _kjc(const char* str, size_t n);
jpayne@69 57
jpayne@69 58 namespace kj {
jpayne@69 59
jpayne@69 60 // Our STL string SFINAE trick does not work with GCC 4.7, but it works with Clang and GCC 4.8, so
jpayne@69 61 // we'll just preprocess it out if not supported.
jpayne@69 62 #if __clang__ || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) || _MSC_VER
jpayne@69 63 #define KJ_COMPILER_SUPPORTS_STL_STRING_INTEROP 1
jpayne@69 64 #endif
jpayne@69 65
jpayne@69 66 // =======================================================================================
jpayne@69 67 // StringPtr -- A NUL-terminated ArrayPtr<const char> containing UTF-8 text.
jpayne@69 68 //
jpayne@69 69 // NUL bytes are allowed to appear before the end of the string. The only requirement is that
jpayne@69 70 // a NUL byte appear immediately after the last byte of the content. This terminator byte is not
jpayne@69 71 // counted in the string's size.
jpayne@69 72
jpayne@69 73 class StringPtr {
jpayne@69 74 public:
jpayne@69 75 inline StringPtr(): content("", 1) {}
jpayne@69 76 inline StringPtr(decltype(nullptr)): content("", 1) {}
jpayne@69 77 inline StringPtr(const char* value KJ_LIFETIMEBOUND): content(value, strlen(value) + 1) {}
jpayne@69 78 inline StringPtr(const char* value KJ_LIFETIMEBOUND, size_t size): content(value, size + 1) {
jpayne@69 79 KJ_IREQUIRE(value[size] == '\0', "StringPtr must be NUL-terminated.");
jpayne@69 80 }
jpayne@69 81 inline StringPtr(const char* begin KJ_LIFETIMEBOUND, const char* end KJ_LIFETIMEBOUND): StringPtr(begin, end - begin) {}
jpayne@69 82 inline StringPtr(String&& value KJ_LIFETIMEBOUND) : StringPtr(value) {}
jpayne@69 83 inline StringPtr(const String& value KJ_LIFETIMEBOUND);
jpayne@69 84 inline StringPtr(const ConstString& value KJ_LIFETIMEBOUND);
jpayne@69 85 StringPtr& operator=(String&& value) = delete;
jpayne@69 86 inline StringPtr& operator=(decltype(nullptr)) {
jpayne@69 87 content = ArrayPtr<const char>("", 1);
jpayne@69 88 return *this;
jpayne@69 89 }
jpayne@69 90
jpayne@69 91 #if __cpp_char8_t
jpayne@69 92 inline StringPtr(const char8_t* value KJ_LIFETIMEBOUND): StringPtr(reinterpret_cast<const char*>(value)) {}
jpayne@69 93 inline StringPtr(const char8_t* value KJ_LIFETIMEBOUND, size_t size)
jpayne@69 94 : StringPtr(reinterpret_cast<const char*>(value), size) {}
jpayne@69 95 inline StringPtr(const char8_t* begin KJ_LIFETIMEBOUND, const char8_t* end KJ_LIFETIMEBOUND)
jpayne@69 96 : StringPtr(reinterpret_cast<const char*>(begin), reinterpret_cast<const char*>(end)) {}
jpayne@69 97 // KJ strings are and always have been UTF-8, so screw this C++20 char8_t stuff.
jpayne@69 98 #endif
jpayne@69 99
jpayne@69 100 #if KJ_COMPILER_SUPPORTS_STL_STRING_INTEROP
jpayne@69 101 template <
jpayne@69 102 typename T,
jpayne@69 103 typename = decltype(instance<T>().c_str()),
jpayne@69 104 typename = decltype(instance<T>().size())>
jpayne@69 105 inline StringPtr(const T& t KJ_LIFETIMEBOUND): StringPtr(t.c_str(), t.size()) {}
jpayne@69 106 // Allow implicit conversion from any class that has a c_str() and a size() method (namely, std::string).
jpayne@69 107 // We use a template trick to detect std::string in order to avoid including the header for
jpayne@69 108 // those who don't want it.
jpayne@69 109 template <
jpayne@69 110 typename T,
jpayne@69 111 typename = decltype(instance<T>().c_str()),
jpayne@69 112 typename = decltype(instance<T>().size())>
jpayne@69 113 inline operator T() const { return {cStr(), size()}; }
jpayne@69 114 // Allow implicit conversion to any class that has a c_str() method and a size() method (namely, std::string).
jpayne@69 115 // We use a template trick to detect std::string in order to avoid including the header for
jpayne@69 116 // those who don't want it.
jpayne@69 117 #endif
jpayne@69 118
jpayne@69 119 inline constexpr operator ArrayPtr<const char>() const;
jpayne@69 120 inline constexpr ArrayPtr<const char> asArray() const;
jpayne@69 121 inline ArrayPtr<const byte> asBytes() const { return asArray().asBytes(); }
jpayne@69 122 // Result does not include NUL terminator.
jpayne@69 123
jpayne@69 124 inline const char* cStr() const { return content.begin(); }
jpayne@69 125 // Returns NUL-terminated string.
jpayne@69 126
jpayne@69 127 inline size_t size() const { return content.size() - 1; }
jpayne@69 128 // Result does not include NUL terminator.
jpayne@69 129
jpayne@69 130 inline char operator[](size_t index) const { return content[index]; }
jpayne@69 131
jpayne@69 132 inline constexpr const char* begin() const { return content.begin(); }
jpayne@69 133 inline constexpr const char* end() const { return content.end() - 1; }
jpayne@69 134
jpayne@69 135 inline constexpr bool operator==(decltype(nullptr)) const { return content.size() <= 1; }
jpayne@69 136 #if !__cpp_impl_three_way_comparison
jpayne@69 137 inline constexpr bool operator!=(decltype(nullptr)) const { return content.size() > 1; }
jpayne@69 138 #endif
jpayne@69 139
jpayne@69 140 inline bool operator==(const StringPtr& other) const;
jpayne@69 141 #if !__cpp_impl_three_way_comparison
jpayne@69 142 inline bool operator!=(const StringPtr& other) const { return !(*this == other); }
jpayne@69 143 #endif
jpayne@69 144 inline bool operator< (const StringPtr& other) const;
jpayne@69 145 inline bool operator> (const StringPtr& other) const { return other < *this; }
jpayne@69 146 inline bool operator<=(const StringPtr& other) const { return !(other < *this); }
jpayne@69 147 inline bool operator>=(const StringPtr& other) const { return !(*this < other); }
jpayne@69 148
jpayne@69 149 inline StringPtr slice(size_t start) const;
jpayne@69 150 inline ArrayPtr<const char> slice(size_t start, size_t end) const;
jpayne@69 151 // A string slice is only NUL-terminated if it is a suffix, so slice() has a one-parameter
jpayne@69 152 // version that assumes end = size().
jpayne@69 153
jpayne@69 154 inline bool startsWith(const StringPtr& other) const { return asArray().startsWith(other);}
jpayne@69 155 inline bool endsWith(const StringPtr& other) const { return asArray().endsWith(other); }
jpayne@69 156
jpayne@69 157 inline Maybe<size_t> findFirst(char c) const { return asArray().findFirst(c); }
jpayne@69 158 inline Maybe<size_t> findLast(char c) const { return asArray().findLast(c); }
jpayne@69 159
jpayne@69 160 template <typename T>
jpayne@69 161 T parseAs() const;
jpayne@69 162 // Parse string as template number type.
jpayne@69 163 // Integer numbers prefixed by "0x" and "0X" are parsed in base 16 (like strtoi with base 0).
jpayne@69 164 // Integer numbers prefixed by "0" are parsed in base 10 (unlike strtoi with base 0).
jpayne@69 165 // Overflowed integer numbers throw exception.
jpayne@69 166 // Overflowed floating numbers return inf.
jpayne@69 167 template <typename T>
jpayne@69 168 Maybe<T> tryParseAs() const;
jpayne@69 169 // Same as parseAs, but rather than throwing an exception we return NULL.
jpayne@69 170
jpayne@69 171 template <typename... Attachments>
jpayne@69 172 ConstString attach(Attachments&&... attachments) const KJ_WARN_UNUSED_RESULT;
jpayne@69 173 ConstString attach() const KJ_WARN_UNUSED_RESULT;
jpayne@69 174 // Like ArrayPtr<T>::attach(), but instead promotes a StringPtr into a ConstString. Generally the
jpayne@69 175 // attachment should be an object that somehow owns the String that the StringPtr is pointing at.
jpayne@69 176
jpayne@69 177 private:
jpayne@69 178 inline explicit constexpr StringPtr(ArrayPtr<const char> content): content(content) {}
jpayne@69 179 friend constexpr StringPtr (::operator "" _kj)(const char* str, size_t n);
jpayne@69 180 friend class LiteralStringConst;
jpayne@69 181
jpayne@69 182 ArrayPtr<const char> content;
jpayne@69 183 friend class SourceLocation;
jpayne@69 184 };
jpayne@69 185
jpayne@69 186 #if !__cpp_impl_three_way_comparison
jpayne@69 187 inline bool operator==(const char* a, const StringPtr& b) { return b == a; }
jpayne@69 188 inline bool operator!=(const char* a, const StringPtr& b) { return b != a; }
jpayne@69 189 #endif
jpayne@69 190
jpayne@69 191 template <> char StringPtr::parseAs<char>() const;
jpayne@69 192 template <> signed char StringPtr::parseAs<signed char>() const;
jpayne@69 193 template <> unsigned char StringPtr::parseAs<unsigned char>() const;
jpayne@69 194 template <> short StringPtr::parseAs<short>() const;
jpayne@69 195 template <> unsigned short StringPtr::parseAs<unsigned short>() const;
jpayne@69 196 template <> int StringPtr::parseAs<int>() const;
jpayne@69 197 template <> unsigned StringPtr::parseAs<unsigned>() const;
jpayne@69 198 template <> long StringPtr::parseAs<long>() const;
jpayne@69 199 template <> unsigned long StringPtr::parseAs<unsigned long>() const;
jpayne@69 200 template <> long long StringPtr::parseAs<long long>() const;
jpayne@69 201 template <> unsigned long long StringPtr::parseAs<unsigned long long>() const;
jpayne@69 202 template <> float StringPtr::parseAs<float>() const;
jpayne@69 203 template <> double StringPtr::parseAs<double>() const;
jpayne@69 204
jpayne@69 205 template <> Maybe<char> StringPtr::tryParseAs<char>() const;
jpayne@69 206 template <> Maybe<signed char> StringPtr::tryParseAs<signed char>() const;
jpayne@69 207 template <> Maybe<unsigned char> StringPtr::tryParseAs<unsigned char>() const;
jpayne@69 208 template <> Maybe<short> StringPtr::tryParseAs<short>() const;
jpayne@69 209 template <> Maybe<unsigned short> StringPtr::tryParseAs<unsigned short>() const;
jpayne@69 210 template <> Maybe<int> StringPtr::tryParseAs<int>() const;
jpayne@69 211 template <> Maybe<unsigned> StringPtr::tryParseAs<unsigned>() const;
jpayne@69 212 template <> Maybe<long> StringPtr::tryParseAs<long>() const;
jpayne@69 213 template <> Maybe<unsigned long> StringPtr::tryParseAs<unsigned long>() const;
jpayne@69 214 template <> Maybe<long long> StringPtr::tryParseAs<long long>() const;
jpayne@69 215 template <> Maybe<unsigned long long> StringPtr::tryParseAs<unsigned long long>() const;
jpayne@69 216 template <> Maybe<float> StringPtr::tryParseAs<float>() const;
jpayne@69 217 template <> Maybe<double> StringPtr::tryParseAs<double>() const;
jpayne@69 218
jpayne@69 219 class LiteralStringConst: public StringPtr {
jpayne@69 220 public:
jpayne@69 221 inline operator ConstString() const;
jpayne@69 222
jpayne@69 223 private:
jpayne@69 224 inline explicit constexpr LiteralStringConst(ArrayPtr<const char> content): StringPtr(content) {}
jpayne@69 225 friend constexpr LiteralStringConst (::operator "" _kjc)(const char* str, size_t n);
jpayne@69 226 };
jpayne@69 227
jpayne@69 228 // =======================================================================================
jpayne@69 229 // String -- A NUL-terminated Array<char> containing UTF-8 text.
jpayne@69 230 //
jpayne@69 231 // NUL bytes are allowed to appear before the end of the string. The only requirement is that
jpayne@69 232 // a NUL byte appear immediately after the last byte of the content. This terminator byte is not
jpayne@69 233 // counted in the string's size.
jpayne@69 234 //
jpayne@69 235 // To allocate a String, you must call kj::heapString(). We do not implement implicit copying to
jpayne@69 236 // the heap because this hides potential inefficiency from the developer.
jpayne@69 237
jpayne@69 238 class String {
jpayne@69 239 public:
jpayne@69 240 String() = default;
jpayne@69 241 inline String(decltype(nullptr)): content(nullptr) {}
jpayne@69 242 inline String(char* value, size_t size, const ArrayDisposer& disposer);
jpayne@69 243 // Does not copy. `size` does not include NUL terminator, but `value` must be NUL-terminated.
jpayne@69 244 inline explicit String(Array<char> buffer);
jpayne@69 245 // Does not copy. Requires `buffer` ends with `\0`.
jpayne@69 246
jpayne@69 247 inline operator ArrayPtr<char>() KJ_LIFETIMEBOUND;
jpayne@69 248 inline operator ArrayPtr<const char>() const KJ_LIFETIMEBOUND;
jpayne@69 249 inline ArrayPtr<char> asArray() KJ_LIFETIMEBOUND;
jpayne@69 250 inline ArrayPtr<const char> asArray() const KJ_LIFETIMEBOUND;
jpayne@69 251 inline ArrayPtr<byte> asBytes() KJ_LIFETIMEBOUND { return asArray().asBytes(); }
jpayne@69 252 inline ArrayPtr<const byte> asBytes() const KJ_LIFETIMEBOUND { return asArray().asBytes(); }
jpayne@69 253 // Result does not include NUL terminator.
jpayne@69 254
jpayne@69 255 inline StringPtr asPtr() const KJ_LIFETIMEBOUND {
jpayne@69 256 // Convenience operator to return a StringPtr.
jpayne@69 257 return StringPtr{*this};
jpayne@69 258 }
jpayne@69 259
jpayne@69 260 inline Array<char> releaseArray() { return kj::mv(content); }
jpayne@69 261 // Disowns the backing array (which includes the NUL terminator) and returns it. The String value
jpayne@69 262 // is clobbered (as if moved away).
jpayne@69 263
jpayne@69 264 inline const char* cStr() const KJ_LIFETIMEBOUND;
jpayne@69 265
jpayne@69 266 inline size_t size() const;
jpayne@69 267 // Result does not include NUL terminator.
jpayne@69 268
jpayne@69 269 inline char operator[](size_t index) const;
jpayne@69 270 inline char& operator[](size_t index) KJ_LIFETIMEBOUND;
jpayne@69 271
jpayne@69 272 inline char* begin() KJ_LIFETIMEBOUND;
jpayne@69 273 inline char* end() KJ_LIFETIMEBOUND;
jpayne@69 274 inline const char* begin() const KJ_LIFETIMEBOUND;
jpayne@69 275 inline const char* end() const KJ_LIFETIMEBOUND;
jpayne@69 276
jpayne@69 277 inline bool operator==(decltype(nullptr)) const { return content.size() <= 1; }
jpayne@69 278 inline bool operator!=(decltype(nullptr)) const { return content.size() > 1; }
jpayne@69 279
jpayne@69 280 inline bool operator==(const StringPtr& other) const { return StringPtr(*this) == other; }
jpayne@69 281 #if !__cpp_impl_three_way_comparison
jpayne@69 282 inline bool operator!=(const StringPtr& other) const { return StringPtr(*this) != other; }
jpayne@69 283 #endif
jpayne@69 284 inline bool operator< (const StringPtr& other) const { return StringPtr(*this) < other; }
jpayne@69 285 inline bool operator> (const StringPtr& other) const { return StringPtr(*this) > other; }
jpayne@69 286 inline bool operator<=(const StringPtr& other) const { return StringPtr(*this) <= other; }
jpayne@69 287 inline bool operator>=(const StringPtr& other) const { return StringPtr(*this) >= other; }
jpayne@69 288
jpayne@69 289 inline bool operator==(const String& other) const { return StringPtr(*this) == StringPtr(other); }
jpayne@69 290 #if !__cpp_impl_three_way_comparison
jpayne@69 291 inline bool operator!=(const String& other) const { return StringPtr(*this) != StringPtr(other); }
jpayne@69 292 #endif
jpayne@69 293 inline bool operator< (const String& other) const { return StringPtr(*this) < StringPtr(other); }
jpayne@69 294 inline bool operator> (const String& other) const { return StringPtr(*this) > StringPtr(other); }
jpayne@69 295 inline bool operator<=(const String& other) const { return StringPtr(*this) <= StringPtr(other); }
jpayne@69 296 inline bool operator>=(const String& other) const { return StringPtr(*this) >= StringPtr(other); }
jpayne@69 297 // Note that if we don't overload for `const String&` specifically, then C++20 will decide that
jpayne@69 298 // comparisons between two strings are ambiguous. (Clang turns this into a warning,
jpayne@69 299 // -Wambiguous-reversed-operator, due to the stupidity...)
jpayne@69 300
jpayne@69 301 inline bool operator==(const ConstString& other) const { return StringPtr(*this) == StringPtr(other); }
jpayne@69 302 #if !__cpp_impl_three_way_comparison
jpayne@69 303 inline bool operator!=(const ConstString& other) const { return StringPtr(*this) != StringPtr(other); }
jpayne@69 304 #endif
jpayne@69 305 inline bool operator< (const ConstString& other) const { return StringPtr(*this) < StringPtr(other); }
jpayne@69 306 inline bool operator> (const ConstString& other) const { return StringPtr(*this) > StringPtr(other); }
jpayne@69 307 inline bool operator<=(const ConstString& other) const { return StringPtr(*this) <= StringPtr(other); }
jpayne@69 308 inline bool operator>=(const ConstString& other) const { return StringPtr(*this) >= StringPtr(other); }
jpayne@69 309
jpayne@69 310 inline bool startsWith(const StringPtr& other) const { return asArray().startsWith(other);}
jpayne@69 311 inline bool endsWith(const StringPtr& other) const { return asArray().endsWith(other); }
jpayne@69 312
jpayne@69 313 inline StringPtr slice(size_t start) const KJ_LIFETIMEBOUND {
jpayne@69 314 return StringPtr(*this).slice(start);
jpayne@69 315 }
jpayne@69 316 inline ArrayPtr<const char> slice(size_t start, size_t end) const KJ_LIFETIMEBOUND {
jpayne@69 317 return StringPtr(*this).slice(start, end);
jpayne@69 318 }
jpayne@69 319
jpayne@69 320 inline Maybe<size_t> findFirst(char c) const { return asArray().findFirst(c); }
jpayne@69 321 inline Maybe<size_t> findLast(char c) const { return asArray().findLast(c); }
jpayne@69 322
jpayne@69 323 template <typename T>
jpayne@69 324 T parseAs() const { return StringPtr(*this).parseAs<T>(); }
jpayne@69 325 // Parse as number
jpayne@69 326
jpayne@69 327 template <typename T>
jpayne@69 328 Maybe<T> tryParseAs() const { return StringPtr(*this).tryParseAs<T>(); }
jpayne@69 329
jpayne@69 330 private:
jpayne@69 331 Array<char> content;
jpayne@69 332 };
jpayne@69 333
jpayne@69 334 // =======================================================================================
jpayne@69 335 // ConstString -- Same as String, but the backing buffer is const.
jpayne@69 336 //
jpayne@69 337 // This has the useful property that it can reference a string literal without allocating
jpayne@69 338 // a copy. Any String can also convert (by move) to ConstString, transferring ownership of
jpayne@69 339 // the buffer.
jpayne@69 340
jpayne@69 341 class ConstString {
jpayne@69 342 public:
jpayne@69 343 ConstString() = default;
jpayne@69 344 inline ConstString(decltype(nullptr)): content(nullptr) {}
jpayne@69 345 inline ConstString(const char* value, size_t size, const ArrayDisposer& disposer);
jpayne@69 346 // Does not copy. `size` does not include NUL terminator, but `value` must be NUL-terminated.
jpayne@69 347 inline explicit ConstString(Array<const char> buffer);
jpayne@69 348 // Does not copy. Requires `buffer` ends with `\0`.
jpayne@69 349 inline explicit ConstString(String&& string): content(string.releaseArray()) {}
jpayne@69 350 // Does not copy. Ownership is transfered.
jpayne@69 351
jpayne@69 352 inline operator ArrayPtr<const char>() const KJ_LIFETIMEBOUND;
jpayne@69 353 inline ArrayPtr<const char> asArray() const KJ_LIFETIMEBOUND;
jpayne@69 354 inline ArrayPtr<const byte> asBytes() const KJ_LIFETIMEBOUND { return asArray().asBytes(); }
jpayne@69 355 // Result does not include NUL terminator.
jpayne@69 356
jpayne@69 357 inline StringPtr asPtr() const KJ_LIFETIMEBOUND {
jpayne@69 358 // Convenience operator to return a StringPtr.
jpayne@69 359 return StringPtr{*this};
jpayne@69 360 }
jpayne@69 361
jpayne@69 362 inline Array<const char> releaseArray() { return kj::mv(content); }
jpayne@69 363 // Disowns the backing array (which includes the NUL terminator) and returns it. The ConstString value
jpayne@69 364 // is clobbered (as if moved away).
jpayne@69 365
jpayne@69 366 inline const char* cStr() const KJ_LIFETIMEBOUND;
jpayne@69 367
jpayne@69 368 inline size_t size() const;
jpayne@69 369 // Result does not include NUL terminator.
jpayne@69 370
jpayne@69 371 inline char operator[](size_t index) const;
jpayne@69 372 inline char& operator[](size_t index) KJ_LIFETIMEBOUND;
jpayne@69 373
jpayne@69 374 inline const char* begin() const KJ_LIFETIMEBOUND;
jpayne@69 375 inline const char* end() const KJ_LIFETIMEBOUND;
jpayne@69 376
jpayne@69 377 inline bool operator==(decltype(nullptr)) const { return content.size() <= 1; }
jpayne@69 378 inline bool operator!=(decltype(nullptr)) const { return content.size() > 1; }
jpayne@69 379
jpayne@69 380 inline bool operator==(const StringPtr& other) const { return StringPtr(*this) == other; }
jpayne@69 381 #if !__cpp_impl_three_way_comparison
jpayne@69 382 inline bool operator!=(const StringPtr& other) const { return StringPtr(*this) != other; }
jpayne@69 383 #endif
jpayne@69 384 inline bool operator< (const StringPtr& other) const { return StringPtr(*this) < other; }
jpayne@69 385 inline bool operator> (const StringPtr& other) const { return StringPtr(*this) > other; }
jpayne@69 386 inline bool operator<=(const StringPtr& other) const { return StringPtr(*this) <= other; }
jpayne@69 387 inline bool operator>=(const StringPtr& other) const { return StringPtr(*this) >= other; }
jpayne@69 388
jpayne@69 389 inline bool operator==(const String& other) const { return StringPtr(*this) == StringPtr(other); }
jpayne@69 390 #if !__cpp_impl_three_way_comparison
jpayne@69 391 inline bool operator!=(const String& other) const { return StringPtr(*this) != StringPtr(other); }
jpayne@69 392 #endif
jpayne@69 393 inline bool operator< (const String& other) const { return StringPtr(*this) < StringPtr(other); }
jpayne@69 394 inline bool operator> (const String& other) const { return StringPtr(*this) > StringPtr(other); }
jpayne@69 395 inline bool operator<=(const String& other) const { return StringPtr(*this) <= StringPtr(other); }
jpayne@69 396 inline bool operator>=(const String& other) const { return StringPtr(*this) >= StringPtr(other); }
jpayne@69 397
jpayne@69 398 inline bool operator==(const ConstString& other) const { return StringPtr(*this) == StringPtr(other); }
jpayne@69 399 #if !__cpp_impl_three_way_comparison
jpayne@69 400 inline bool operator!=(const ConstString& other) const { return StringPtr(*this) != StringPtr(other); }
jpayne@69 401 #endif
jpayne@69 402 inline bool operator< (const ConstString& other) const { return StringPtr(*this) < StringPtr(other); }
jpayne@69 403 inline bool operator> (const ConstString& other) const { return StringPtr(*this) > StringPtr(other); }
jpayne@69 404 inline bool operator<=(const ConstString& other) const { return StringPtr(*this) <= StringPtr(other); }
jpayne@69 405 inline bool operator>=(const ConstString& other) const { return StringPtr(*this) >= StringPtr(other); }
jpayne@69 406 // Note that if we don't overload for `const ConstString&` specifically, then C++20 will decide that
jpayne@69 407 // comparisons between two strings are ambiguous. (Clang turns this into a warning,
jpayne@69 408 // -Wambiguous-reversed-operator, due to the stupidity...)
jpayne@69 409
jpayne@69 410 inline bool startsWith(const StringPtr& other) const { return asArray().startsWith(other);}
jpayne@69 411 inline bool endsWith(const StringPtr& other) const { return asArray().endsWith(other); }
jpayne@69 412
jpayne@69 413 inline StringPtr slice(size_t start) const KJ_LIFETIMEBOUND {
jpayne@69 414 return StringPtr(*this).slice(start);
jpayne@69 415 }
jpayne@69 416 inline ArrayPtr<const char> slice(size_t start, size_t end) const KJ_LIFETIMEBOUND {
jpayne@69 417 return StringPtr(*this).slice(start, end);
jpayne@69 418 }
jpayne@69 419
jpayne@69 420 inline Maybe<size_t> findFirst(char c) const { return asArray().findFirst(c); }
jpayne@69 421 inline Maybe<size_t> findLast(char c) const { return asArray().findLast(c); }
jpayne@69 422
jpayne@69 423 template <typename T>
jpayne@69 424 T parseAs() const { return StringPtr(*this).parseAs<T>(); }
jpayne@69 425 // Parse as number
jpayne@69 426
jpayne@69 427 template <typename T>
jpayne@69 428 Maybe<T> tryParseAs() const { return StringPtr(*this).tryParseAs<T>(); }
jpayne@69 429
jpayne@69 430 private:
jpayne@69 431 Array<const char> content;
jpayne@69 432 };
jpayne@69 433
jpayne@69 434 #if !__cpp_impl_three_way_comparison
jpayne@69 435 inline bool operator==(const char* a, const String& b) { return b == a; }
jpayne@69 436 inline bool operator!=(const char* a, const String& b) { return b != a; }
jpayne@69 437 #endif
jpayne@69 438
jpayne@69 439 String heapString(size_t size);
jpayne@69 440 // Allocate a String of the given size on the heap, not including NUL terminator. The NUL
jpayne@69 441 // terminator will be initialized automatically but the rest of the content is not initialized.
jpayne@69 442
jpayne@69 443 String heapString(const char* value);
jpayne@69 444 String heapString(const char* value, size_t size);
jpayne@69 445 String heapString(StringPtr value);
jpayne@69 446 String heapString(const String& value);
jpayne@69 447 String heapString(ArrayPtr<const char> value);
jpayne@69 448 // Allocates a copy of the given value on the heap.
jpayne@69 449
jpayne@69 450 // =======================================================================================
jpayne@69 451 // Magic str() function which transforms parameters to text and concatenates them into one big
jpayne@69 452 // String.
jpayne@69 453
jpayne@69 454 namespace _ { // private
jpayne@69 455
jpayne@69 456 inline size_t sum(std::initializer_list<size_t> nums) {
jpayne@69 457 size_t result = 0;
jpayne@69 458 for (auto num: nums) {
jpayne@69 459 result += num;
jpayne@69 460 }
jpayne@69 461 return result;
jpayne@69 462 }
jpayne@69 463
jpayne@69 464 inline char* fill(char* ptr) { return ptr; }
jpayne@69 465 inline char* fillLimited(char* ptr, char* limit) { return ptr; }
jpayne@69 466
jpayne@69 467 template <typename... Rest>
jpayne@69 468 char* fill(char* __restrict__ target, const StringTree& first, Rest&&... rest);
jpayne@69 469 template <typename... Rest>
jpayne@69 470 char* fillLimited(char* __restrict__ target, char* limit, const StringTree& first, Rest&&... rest);
jpayne@69 471 // Make str() work with stringifiers that return StringTree by patching fill().
jpayne@69 472 //
jpayne@69 473 // Defined in string-tree.h.
jpayne@69 474
jpayne@69 475 template <typename First, typename... Rest>
jpayne@69 476 char* fill(char* __restrict__ target, const First& first, Rest&&... rest) {
jpayne@69 477 auto i = first.begin();
jpayne@69 478 auto end = first.end();
jpayne@69 479 while (i != end) {
jpayne@69 480 *target++ = *i++;
jpayne@69 481 }
jpayne@69 482 return fill(target, kj::fwd<Rest>(rest)...);
jpayne@69 483 }
jpayne@69 484
jpayne@69 485 template <typename... Params>
jpayne@69 486 String concat(Params&&... params) {
jpayne@69 487 // Concatenate a bunch of containers into a single Array. The containers can be anything that
jpayne@69 488 // is iterable and whose elements can be converted to `char`.
jpayne@69 489
jpayne@69 490 String result = heapString(sum({params.size()...}));
jpayne@69 491 fill(result.begin(), kj::fwd<Params>(params)...);
jpayne@69 492 return result;
jpayne@69 493 }
jpayne@69 494
jpayne@69 495 inline String concat(String&& arr) {
jpayne@69 496 return kj::mv(arr);
jpayne@69 497 }
jpayne@69 498
jpayne@69 499 template <typename First, typename... Rest>
jpayne@69 500 char* fillLimited(char* __restrict__ target, char* limit, const First& first, Rest&&... rest) {
jpayne@69 501 auto i = first.begin();
jpayne@69 502 auto end = first.end();
jpayne@69 503 while (i != end) {
jpayne@69 504 if (target == limit) return target;
jpayne@69 505 *target++ = *i++;
jpayne@69 506 }
jpayne@69 507 return fillLimited(target, limit, kj::fwd<Rest>(rest)...);
jpayne@69 508 }
jpayne@69 509
jpayne@69 510 template <typename T>
jpayne@69 511 class Delimited;
jpayne@69 512 // Delimits a sequence of type T with a string delimiter. Implements kj::delimited().
jpayne@69 513
jpayne@69 514 template <typename T, typename... Rest>
jpayne@69 515 char* fill(char* __restrict__ target, Delimited<T>&& first, Rest&&... rest);
jpayne@69 516 template <typename T, typename... Rest>
jpayne@69 517 char* fillLimited(char* __restrict__ target, char* limit, Delimited<T>&& first,Rest&&... rest);
jpayne@69 518 template <typename T, typename... Rest>
jpayne@69 519 char* fill(char* __restrict__ target, Delimited<T>& first, Rest&&... rest);
jpayne@69 520 template <typename T, typename... Rest>
jpayne@69 521 char* fillLimited(char* __restrict__ target, char* limit, Delimited<T>& first,Rest&&... rest);
jpayne@69 522 // As with StringTree, we special-case Delimited<T>.
jpayne@69 523
jpayne@69 524 struct Stringifier {
jpayne@69 525 // This is a dummy type with only one instance: STR (below). To make an arbitrary type
jpayne@69 526 // stringifiable, define `operator*(Stringifier, T)` to return an iterable container of `char`.
jpayne@69 527 // The container type must have a `size()` method. Be sure to declare the operator in the same
jpayne@69 528 // namespace as `T` **or** in the global scope.
jpayne@69 529 //
jpayne@69 530 // A more usual way to accomplish what we're doing here would be to require that you define
jpayne@69 531 // a function like `toString(T)` and then rely on argument-dependent lookup. However, this has
jpayne@69 532 // the problem that it pollutes other people's namespaces and even the global namespace. For
jpayne@69 533 // example, some other project may already have functions called `toString` which do something
jpayne@69 534 // different. Declaring `operator*` with `Stringifier` as the left operand cannot conflict with
jpayne@69 535 // anything.
jpayne@69 536
jpayne@69 537 inline ArrayPtr<const char> operator*(ArrayPtr<const char> s) const { return s; }
jpayne@69 538 inline ArrayPtr<const char> operator*(ArrayPtr<char> s) const { return s; }
jpayne@69 539 inline ArrayPtr<const char> operator*(const Array<const char>& s) const KJ_LIFETIMEBOUND {
jpayne@69 540 return s;
jpayne@69 541 }
jpayne@69 542 inline ArrayPtr<const char> operator*(const Array<char>& s) const KJ_LIFETIMEBOUND { return s; }
jpayne@69 543 template<size_t n>
jpayne@69 544 inline ArrayPtr<const char> operator*(const CappedArray<char, n>& s) const KJ_LIFETIMEBOUND {
jpayne@69 545 return s;
jpayne@69 546 }
jpayne@69 547 template<size_t n>
jpayne@69 548 inline ArrayPtr<const char> operator*(const FixedArray<char, n>& s) const KJ_LIFETIMEBOUND {
jpayne@69 549 return s;
jpayne@69 550 }
jpayne@69 551 inline ArrayPtr<const char> operator*(const char* s) const KJ_LIFETIMEBOUND {
jpayne@69 552 return arrayPtr(s, strlen(s));
jpayne@69 553 }
jpayne@69 554 #if __cpp_char8_t
jpayne@69 555 inline ArrayPtr<const char> operator*(const char8_t* s) const KJ_LIFETIMEBOUND {
jpayne@69 556 return operator*(reinterpret_cast<const char*>(s));
jpayne@69 557 }
jpayne@69 558 #endif
jpayne@69 559 inline ArrayPtr<const char> operator*(const String& s) const KJ_LIFETIMEBOUND {
jpayne@69 560 return s.asArray();
jpayne@69 561 }
jpayne@69 562 inline ArrayPtr<const char> operator*(const StringPtr& s) const { return s.asArray(); }
jpayne@69 563 inline ArrayPtr<const char> operator*(const ConstString& s) const { return s.asArray(); }
jpayne@69 564
jpayne@69 565 inline Range<char> operator*(const Range<char>& r) const { return r; }
jpayne@69 566 inline Repeat<char> operator*(const Repeat<char>& r) const { return r; }
jpayne@69 567
jpayne@69 568 inline FixedArray<char, 1> operator*(char c) const {
jpayne@69 569 FixedArray<char, 1> result;
jpayne@69 570 result[0] = c;
jpayne@69 571 return result;
jpayne@69 572 }
jpayne@69 573
jpayne@69 574 StringPtr operator*(decltype(nullptr)) const;
jpayne@69 575 StringPtr operator*(bool b) const;
jpayne@69 576
jpayne@69 577 CappedArray<char, 5> operator*(signed char i) const;
jpayne@69 578 CappedArray<char, 5> operator*(unsigned char i) const;
jpayne@69 579 CappedArray<char, sizeof(short) * 3 + 2> operator*(short i) const;
jpayne@69 580 CappedArray<char, sizeof(unsigned short) * 3 + 2> operator*(unsigned short i) const;
jpayne@69 581 CappedArray<char, sizeof(int) * 3 + 2> operator*(int i) const;
jpayne@69 582 CappedArray<char, sizeof(unsigned int) * 3 + 2> operator*(unsigned int i) const;
jpayne@69 583 CappedArray<char, sizeof(long) * 3 + 2> operator*(long i) const;
jpayne@69 584 CappedArray<char, sizeof(unsigned long) * 3 + 2> operator*(unsigned long i) const;
jpayne@69 585 CappedArray<char, sizeof(long long) * 3 + 2> operator*(long long i) const;
jpayne@69 586 CappedArray<char, sizeof(unsigned long long) * 3 + 2> operator*(unsigned long long i) const;
jpayne@69 587 CappedArray<char, 24> operator*(float f) const;
jpayne@69 588 CappedArray<char, 32> operator*(double f) const;
jpayne@69 589 CappedArray<char, sizeof(const void*) * 2 + 1> operator*(const void* s) const;
jpayne@69 590
jpayne@69 591 #if KJ_COMPILER_SUPPORTS_STL_STRING_INTEROP // supports expression SFINAE?
jpayne@69 592 template <typename T, typename Result = decltype(instance<T>().toString())>
jpayne@69 593 inline Result operator*(T&& value) const { return kj::fwd<T>(value).toString(); }
jpayne@69 594 #endif
jpayne@69 595 };
jpayne@69 596 static KJ_CONSTEXPR(const) Stringifier STR = Stringifier();
jpayne@69 597
jpayne@69 598 } // namespace _ (private)
jpayne@69 599
jpayne@69 600 template <typename T>
jpayne@69 601 auto toCharSequence(T&& value) -> decltype(_::STR * kj::fwd<T>(value)) {
jpayne@69 602 // Returns an iterable of chars that represent a textual representation of the value, suitable
jpayne@69 603 // for debugging.
jpayne@69 604 //
jpayne@69 605 // Most users should use str() instead, but toCharSequence() may occasionally be useful to avoid
jpayne@69 606 // heap allocation overhead that str() implies.
jpayne@69 607 //
jpayne@69 608 // To specialize this function for your type, see KJ_STRINGIFY.
jpayne@69 609
jpayne@69 610 return _::STR * kj::fwd<T>(value);
jpayne@69 611 }
jpayne@69 612
jpayne@69 613 CappedArray<char, sizeof(unsigned char) * 2 + 1> hex(unsigned char i);
jpayne@69 614 CappedArray<char, sizeof(unsigned short) * 2 + 1> hex(unsigned short i);
jpayne@69 615 CappedArray<char, sizeof(unsigned int) * 2 + 1> hex(unsigned int i);
jpayne@69 616 CappedArray<char, sizeof(unsigned long) * 2 + 1> hex(unsigned long i);
jpayne@69 617 CappedArray<char, sizeof(unsigned long long) * 2 + 1> hex(unsigned long long i);
jpayne@69 618
jpayne@69 619 template <typename... Params>
jpayne@69 620 String str(Params&&... params) {
jpayne@69 621 // Magic function which builds a string from a bunch of arbitrary values. Example:
jpayne@69 622 // str(1, " / ", 2, " = ", 0.5)
jpayne@69 623 // returns:
jpayne@69 624 // "1 / 2 = 0.5"
jpayne@69 625 // To teach `str` how to stringify a type, see `Stringifier`.
jpayne@69 626
jpayne@69 627 return _::concat(toCharSequence(kj::fwd<Params>(params))...);
jpayne@69 628 }
jpayne@69 629
jpayne@69 630 inline String str(String&& s) { return mv(s); }
jpayne@69 631 // Overload to prevent redundant allocation.
jpayne@69 632
jpayne@69 633 template <typename T>
jpayne@69 634 _::Delimited<T> delimited(T&& arr, kj::StringPtr delim);
jpayne@69 635 // Use to stringify an array.
jpayne@69 636
jpayne@69 637 template <typename T>
jpayne@69 638 String strArray(T&& arr, const char* delim) {
jpayne@69 639 size_t delimLen = strlen(delim);
jpayne@69 640 KJ_STACK_ARRAY(decltype(_::STR * arr[0]), pieces, kj::size(arr), 8, 32);
jpayne@69 641 size_t size = 0;
jpayne@69 642 for (size_t i = 0; i < kj::size(arr); i++) {
jpayne@69 643 if (i > 0) size += delimLen;
jpayne@69 644 pieces[i] = _::STR * arr[i];
jpayne@69 645 size += pieces[i].size();
jpayne@69 646 }
jpayne@69 647
jpayne@69 648 String result = heapString(size);
jpayne@69 649 char* pos = result.begin();
jpayne@69 650 for (size_t i = 0; i < kj::size(arr); i++) {
jpayne@69 651 if (i > 0) {
jpayne@69 652 memcpy(pos, delim, delimLen);
jpayne@69 653 pos += delimLen;
jpayne@69 654 }
jpayne@69 655 pos = _::fill(pos, pieces[i]);
jpayne@69 656 }
jpayne@69 657 return result;
jpayne@69 658 }
jpayne@69 659
jpayne@69 660 template <typename... Params>
jpayne@69 661 StringPtr strPreallocated(ArrayPtr<char> buffer, Params&&... params) {
jpayne@69 662 // Like str() but writes into a preallocated buffer. If the buffer is not long enough, the result
jpayne@69 663 // is truncated (but still NUL-terminated).
jpayne@69 664 //
jpayne@69 665 // This can be used like:
jpayne@69 666 //
jpayne@69 667 // char buffer[256];
jpayne@69 668 // StringPtr text = strPreallocated(buffer, params...);
jpayne@69 669 //
jpayne@69 670 // This is useful for optimization. It can also potentially be used safely in async signal
jpayne@69 671 // handlers. HOWEVER, to use in an async signal handler, all of the stringifiers for the inputs
jpayne@69 672 // must also be signal-safe. KJ guarantees signal safety when stringifying any built-in integer
jpayne@69 673 // type (but NOT floating-points), basic char/byte sequences (ArrayPtr<byte>, String, etc.), as
jpayne@69 674 // well as Array<T> as long as T can also be stringified safely. To safely stringify a delimited
jpayne@69 675 // array, you must use kj::delimited(arr, delim) rather than the deprecated
jpayne@69 676 // kj::strArray(arr, delim).
jpayne@69 677
jpayne@69 678 char* end = _::fillLimited(buffer.begin(), buffer.end() - 1,
jpayne@69 679 toCharSequence(kj::fwd<Params>(params))...);
jpayne@69 680 *end = '\0';
jpayne@69 681 return StringPtr(buffer.begin(), end);
jpayne@69 682 }
jpayne@69 683
jpayne@69 684 template <typename T, typename = decltype(toCharSequence(kj::instance<T&>()))>
jpayne@69 685 inline _::Delimited<ArrayPtr<T>> operator*(const _::Stringifier&, ArrayPtr<T> arr) {
jpayne@69 686 return _::Delimited<ArrayPtr<T>>(arr, ", ");
jpayne@69 687 }
jpayne@69 688
jpayne@69 689 template <typename T, typename = decltype(toCharSequence(kj::instance<const T&>()))>
jpayne@69 690 inline _::Delimited<ArrayPtr<const T>> operator*(const _::Stringifier&, const Array<T>& arr) {
jpayne@69 691 return _::Delimited<ArrayPtr<const T>>(arr, ", ");
jpayne@69 692 }
jpayne@69 693
jpayne@69 694 #define KJ_STRINGIFY(...) operator*(::kj::_::Stringifier, __VA_ARGS__)
jpayne@69 695 // Defines a stringifier for a custom type. Example:
jpayne@69 696 //
jpayne@69 697 // class Foo {...};
jpayne@69 698 // inline StringPtr KJ_STRINGIFY(const Foo& foo) { return foo.name(); }
jpayne@69 699 // // or perhaps
jpayne@69 700 // inline String KJ_STRINGIFY(const Foo& foo) { return kj::str(foo.fld1(), ",", foo.fld2()); }
jpayne@69 701 //
jpayne@69 702 // This allows Foo to be passed to str().
jpayne@69 703 //
jpayne@69 704 // The function should be declared either in the same namespace as the target type or in the global
jpayne@69 705 // namespace. It can return any type which is an iterable container of chars.
jpayne@69 706
jpayne@69 707 // =======================================================================================
jpayne@69 708 // Inline implementation details.
jpayne@69 709
jpayne@69 710 inline StringPtr::StringPtr(const String& value): content(value.cStr(), value.size() + 1) {}
jpayne@69 711 inline StringPtr::StringPtr(const ConstString& value): content(value.cStr(), value.size() + 1) {}
jpayne@69 712
jpayne@69 713 inline constexpr StringPtr::operator ArrayPtr<const char>() const {
jpayne@69 714 return ArrayPtr<const char>(content.begin(), content.size() - 1);
jpayne@69 715 }
jpayne@69 716
jpayne@69 717 inline constexpr ArrayPtr<const char> StringPtr::asArray() const {
jpayne@69 718 return ArrayPtr<const char>(content.begin(), content.size() - 1);
jpayne@69 719 }
jpayne@69 720
jpayne@69 721 inline bool StringPtr::operator==(const StringPtr& other) const {
jpayne@69 722 return content.size() == other.content.size() &&
jpayne@69 723 memcmp(content.begin(), other.content.begin(), content.size() - 1) == 0;
jpayne@69 724 }
jpayne@69 725
jpayne@69 726 inline bool StringPtr::operator<(const StringPtr& other) const {
jpayne@69 727 bool shorter = content.size() < other.content.size();
jpayne@69 728 int cmp = memcmp(content.begin(), other.content.begin(),
jpayne@69 729 shorter ? content.size() : other.content.size());
jpayne@69 730 return cmp < 0 || (cmp == 0 && shorter);
jpayne@69 731 }
jpayne@69 732
jpayne@69 733 inline StringPtr StringPtr::slice(size_t start) const {
jpayne@69 734 return StringPtr(content.slice(start, content.size()));
jpayne@69 735 }
jpayne@69 736 inline ArrayPtr<const char> StringPtr::slice(size_t start, size_t end) const {
jpayne@69 737 return content.slice(start, end);
jpayne@69 738 }
jpayne@69 739
jpayne@69 740 inline LiteralStringConst::operator ConstString() const {
jpayne@69 741 return ConstString(begin(), size(), NullArrayDisposer::instance);
jpayne@69 742 }
jpayne@69 743
jpayne@69 744 inline ConstString StringPtr::attach() const {
jpayne@69 745 // This is meant as a roundabout way to make a ConstString from a StringPtr
jpayne@69 746 return ConstString(begin(), size(), NullArrayDisposer::instance);
jpayne@69 747 }
jpayne@69 748
jpayne@69 749 template <typename... Attachments>
jpayne@69 750 inline ConstString StringPtr::attach(Attachments&&... attachments) const {
jpayne@69 751 return ConstString { content.attach(kj::fwd<Attachments>(attachments)...) };
jpayne@69 752 }
jpayne@69 753
jpayne@69 754 inline String::operator ArrayPtr<char>() {
jpayne@69 755 return content == nullptr ? ArrayPtr<char>(nullptr) : content.slice(0, content.size() - 1);
jpayne@69 756 }
jpayne@69 757 inline String::operator ArrayPtr<const char>() const {
jpayne@69 758 return content == nullptr ? ArrayPtr<const char>(nullptr) : content.slice(0, content.size() - 1);
jpayne@69 759 }
jpayne@69 760 inline ConstString::operator ArrayPtr<const char>() const {
jpayne@69 761 return content == nullptr ? ArrayPtr<const char>(nullptr) : content.slice(0, content.size() - 1);
jpayne@69 762 }
jpayne@69 763
jpayne@69 764 inline ArrayPtr<char> String::asArray() {
jpayne@69 765 return content == nullptr ? ArrayPtr<char>(nullptr) : content.slice(0, content.size() - 1);
jpayne@69 766 }
jpayne@69 767 inline ArrayPtr<const char> String::asArray() const {
jpayne@69 768 return content == nullptr ? ArrayPtr<const char>(nullptr) : content.slice(0, content.size() - 1);
jpayne@69 769 }
jpayne@69 770 inline ArrayPtr<const char> ConstString::asArray() const {
jpayne@69 771 return content == nullptr ? ArrayPtr<const char>(nullptr) : content.slice(0, content.size() - 1);
jpayne@69 772 }
jpayne@69 773
jpayne@69 774 inline const char* String::cStr() const { return content == nullptr ? "" : content.begin(); }
jpayne@69 775 inline const char* ConstString::cStr() const { return content == nullptr ? "" : content.begin(); }
jpayne@69 776
jpayne@69 777 inline size_t String::size() const { return content == nullptr ? 0 : content.size() - 1; }
jpayne@69 778 inline size_t ConstString::size() const { return content == nullptr ? 0 : content.size() - 1; }
jpayne@69 779
jpayne@69 780 inline char String::operator[](size_t index) const { return content[index]; }
jpayne@69 781 inline char& String::operator[](size_t index) { return content[index]; }
jpayne@69 782 inline char ConstString::operator[](size_t index) const { return content[index]; }
jpayne@69 783
jpayne@69 784 inline char* String::begin() { return content == nullptr ? nullptr : content.begin(); }
jpayne@69 785 inline char* String::end() { return content == nullptr ? nullptr : content.end() - 1; }
jpayne@69 786 inline const char* String::begin() const { return content == nullptr ? nullptr : content.begin(); }
jpayne@69 787 inline const char* String::end() const { return content == nullptr ? nullptr : content.end() - 1; }
jpayne@69 788 inline const char* ConstString::begin() const { return content == nullptr ? nullptr : content.begin(); }
jpayne@69 789 inline const char* ConstString::end() const { return content == nullptr ? nullptr : content.end() - 1; }
jpayne@69 790
jpayne@69 791 inline String::String(char* value, size_t size, const ArrayDisposer& disposer)
jpayne@69 792 : content(value, size + 1, disposer) {
jpayne@69 793 KJ_IREQUIRE(value[size] == '\0', "String must be NUL-terminated.");
jpayne@69 794 }
jpayne@69 795 inline ConstString::ConstString(const char* value, size_t size, const ArrayDisposer& disposer)
jpayne@69 796 : content(value, size + 1, disposer) {
jpayne@69 797 KJ_IREQUIRE(value[size] == '\0', "String must be NUL-terminated.");
jpayne@69 798 }
jpayne@69 799
jpayne@69 800 inline String::String(Array<char> buffer): content(kj::mv(buffer)) {
jpayne@69 801 KJ_IREQUIRE(content.size() > 0 && content.back() == '\0', "String must be NUL-terminated.");
jpayne@69 802 }
jpayne@69 803 inline ConstString::ConstString(Array<const char> buffer): content(kj::mv(buffer)) {
jpayne@69 804 KJ_IREQUIRE(content.size() > 0 && content.back() == '\0', "String must be NUL-terminated.");
jpayne@69 805 }
jpayne@69 806
jpayne@69 807 inline String heapString(const char* value) {
jpayne@69 808 return heapString(value, strlen(value));
jpayne@69 809 }
jpayne@69 810 inline String heapString(StringPtr value) {
jpayne@69 811 return heapString(value.begin(), value.size());
jpayne@69 812 }
jpayne@69 813 inline String heapString(const String& value) {
jpayne@69 814 return heapString(value.begin(), value.size());
jpayne@69 815 }
jpayne@69 816 inline String heapString(ArrayPtr<const char> value) {
jpayne@69 817 return heapString(value.begin(), value.size());
jpayne@69 818 }
jpayne@69 819
jpayne@69 820 namespace _ { // private
jpayne@69 821
jpayne@69 822 template <typename T>
jpayne@69 823 class Delimited {
jpayne@69 824 public:
jpayne@69 825 Delimited(T array, kj::StringPtr delimiter)
jpayne@69 826 : array(kj::fwd<T>(array)), delimiter(delimiter) {}
jpayne@69 827
jpayne@69 828 // TODO(someday): In theory we should support iteration as a character sequence, but the iterator
jpayne@69 829 // will be pretty complicated.
jpayne@69 830
jpayne@69 831 size_t size() {
jpayne@69 832 ensureStringifiedInitialized();
jpayne@69 833
jpayne@69 834 size_t result = 0;
jpayne@69 835 bool first = true;
jpayne@69 836 for (auto& e: stringified) {
jpayne@69 837 if (first) {
jpayne@69 838 first = false;
jpayne@69 839 } else {
jpayne@69 840 result += delimiter.size();
jpayne@69 841 }
jpayne@69 842 result += e.size();
jpayne@69 843 }
jpayne@69 844 return result;
jpayne@69 845 }
jpayne@69 846
jpayne@69 847 char* flattenTo(char* __restrict__ target) {
jpayne@69 848 ensureStringifiedInitialized();
jpayne@69 849
jpayne@69 850 bool first = true;
jpayne@69 851 for (auto& elem: stringified) {
jpayne@69 852 if (first) {
jpayne@69 853 first = false;
jpayne@69 854 } else {
jpayne@69 855 target = fill(target, delimiter);
jpayne@69 856 }
jpayne@69 857 target = fill(target, elem);
jpayne@69 858 }
jpayne@69 859 return target;
jpayne@69 860 }
jpayne@69 861
jpayne@69 862 char* flattenTo(char* __restrict__ target, char* limit) {
jpayne@69 863 // This is called in the strPreallocated(). We want to avoid allocation. size() will not have
jpayne@69 864 // been called in this case, so hopefully `stringified` is still uninitialized. We will
jpayne@69 865 // stringify each item and immediately use it.
jpayne@69 866 bool first = true;
jpayne@69 867 for (auto&& elem: array) {
jpayne@69 868 if (target == limit) return target;
jpayne@69 869 if (first) {
jpayne@69 870 first = false;
jpayne@69 871 } else {
jpayne@69 872 target = fillLimited(target, limit, delimiter);
jpayne@69 873 }
jpayne@69 874 target = fillLimited(target, limit, kj::toCharSequence(elem));
jpayne@69 875 }
jpayne@69 876 return target;
jpayne@69 877 }
jpayne@69 878
jpayne@69 879 private:
jpayne@69 880 typedef decltype(toCharSequence(*instance<T>().begin())) StringifiedItem;
jpayne@69 881 T array;
jpayne@69 882 kj::StringPtr delimiter;
jpayne@69 883 Array<StringifiedItem> stringified;
jpayne@69 884
jpayne@69 885 void ensureStringifiedInitialized() {
jpayne@69 886 if (array.size() > 0 && stringified.size() == 0) {
jpayne@69 887 stringified = KJ_MAP(e, array) { return toCharSequence(e); };
jpayne@69 888 }
jpayne@69 889 }
jpayne@69 890 };
jpayne@69 891
jpayne@69 892 template <typename T, typename... Rest>
jpayne@69 893 char* fill(char* __restrict__ target, Delimited<T>&& first, Rest&&... rest) {
jpayne@69 894 target = first.flattenTo(target);
jpayne@69 895 return fill(target, kj::fwd<Rest>(rest)...);
jpayne@69 896 }
jpayne@69 897 template <typename T, typename... Rest>
jpayne@69 898 char* fillLimited(char* __restrict__ target, char* limit, Delimited<T>&& first, Rest&&... rest) {
jpayne@69 899 target = first.flattenTo(target, limit);
jpayne@69 900 return fillLimited(target, limit, kj::fwd<Rest>(rest)...);
jpayne@69 901 }
jpayne@69 902 template <typename T, typename... Rest>
jpayne@69 903 char* fill(char* __restrict__ target, Delimited<T>& first, Rest&&... rest) {
jpayne@69 904 target = first.flattenTo(target);
jpayne@69 905 return fill(target, kj::fwd<Rest>(rest)...);
jpayne@69 906 }
jpayne@69 907 template <typename T, typename... Rest>
jpayne@69 908 char* fillLimited(char* __restrict__ target, char* limit, Delimited<T>& first, Rest&&... rest) {
jpayne@69 909 target = first.flattenTo(target, limit);
jpayne@69 910 return fillLimited(target, limit, kj::fwd<Rest>(rest)...);
jpayne@69 911 }
jpayne@69 912
jpayne@69 913 template <typename T>
jpayne@69 914 inline Delimited<T>&& KJ_STRINGIFY(Delimited<T>&& delimited) { return kj::mv(delimited); }
jpayne@69 915 template <typename T>
jpayne@69 916 inline const Delimited<T>& KJ_STRINGIFY(const Delimited<T>& delimited) { return delimited; }
jpayne@69 917
jpayne@69 918 } // namespace _ (private)
jpayne@69 919
jpayne@69 920 template <typename T>
jpayne@69 921 _::Delimited<T> delimited(T&& arr, kj::StringPtr delim) {
jpayne@69 922 return _::Delimited<T>(kj::fwd<T>(arr), delim);
jpayne@69 923 }
jpayne@69 924
jpayne@69 925 } // namespace kj
jpayne@69 926
jpayne@69 927 constexpr kj::StringPtr operator "" _kj(const char* str, size_t n) {
jpayne@69 928 return kj::StringPtr(kj::ArrayPtr<const char>(str, n + 1));
jpayne@69 929 };
jpayne@69 930
jpayne@69 931 constexpr kj::LiteralStringConst operator "" _kjc(const char* str, size_t n) {
jpayne@69 932 return kj::LiteralStringConst(kj::ArrayPtr<const char>(str, n + 1));
jpayne@69 933 };
jpayne@69 934
jpayne@69 935 KJ_END_HEADER