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