mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 20:42:43 +00:00 
			
		
		
		
	 a3e82eaad3
			
		
	
	
		a3e82eaad3
		
	
	
	
	
		
			
			DeprecatedString (formerly String) has been with us since the start, and it has served us well. However, it has a number of shortcomings that I'd like to address. Some of these issues are hard if not impossible to solve incrementally inside of DeprecatedString, so instead of doing that, let's build a new String class and then incrementally move over to it instead. Problems in DeprecatedString: - It assumes string allocation never fails. This makes it impossible to use in allocation-sensitive contexts, and is the reason we had to ban DeprecatedString from the kernel entirely. - The awkward null state. DeprecatedString can be null. It's different from the empty state, although null strings are considered empty. All code is immediately nicer when using Optional<DeprecatedString> but DeprecatedString came before Optional, which is how we ended up like this. - The encoding of the underlying data is ambiguous. For the most part, we use it as if it's always UTF-8, but there have been cases where we pass around strings in other encodings (e.g ISO8859-1) - operator[] and length() are used to iterate over DeprecatedString one byte at a time. This is done all over the codebase, and will *not* give the right results unless the string is all ASCII. How we solve these issues in the new String: - Functions that may allocate now return ErrorOr<String> so that ENOMEM errors can be passed to the caller. - String has no null state. Use Optional<String> when needed. - String is always UTF-8. This is validated when constructing a String. We may need to add a bypass for this in the future, for cases where you have a known-good string, but for now: validate all the things! - There is no operator[] or length(). You can get the underlying data with bytes(), but for iterating over code points, you should be using an UTF-8 iterator. Furthermore, it has two nifty new features: - String implements a small string optimization (SSO) for strings that can fit entirely within a pointer. This means up to 3 bytes on 32-bit platforms, and 7 bytes on 64-bit platforms. Such small strings will not be heap-allocated. - String can create substrings without making a deep copy of the substring. Instead, the superstring gets +1 refcount from the substring, and it acts like a view into the superstring. To make substrings like this, use the substring_with_shared_superstring() API. One caveat: - String does not guarantee that the underlying data is null-terminated like DeprecatedString does today. While this was nifty in a handful of places where we were calling C functions, it did stand in the way of shared-superstring substrings.
		
			
				
	
	
		
			120 lines
		
	
	
	
		
			3.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			120 lines
		
	
	
	
		
			3.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
 | |
|  * Copyright (c) 2020, Fei Wu <f.eiwu@yahoo.com>
 | |
|  *
 | |
|  * SPDX-License-Identifier: BSD-2-Clause
 | |
|  */
 | |
| 
 | |
| #pragma once
 | |
| 
 | |
| #include <AK/Concepts.h>
 | |
| #include <AK/EnumBits.h>
 | |
| #include <AK/Forward.h>
 | |
| 
 | |
| namespace AK {
 | |
| 
 | |
| namespace Detail {
 | |
| template<Concepts::AnyString T, Concepts::AnyString U>
 | |
| inline constexpr bool IsHashCompatible<T, U> = true;
 | |
| }
 | |
| 
 | |
| enum class CaseSensitivity {
 | |
|     CaseInsensitive,
 | |
|     CaseSensitive,
 | |
| };
 | |
| 
 | |
| enum class ReplaceMode {
 | |
|     All,
 | |
|     FirstOnly,
 | |
| };
 | |
| 
 | |
| enum class TrimMode {
 | |
|     Left,
 | |
|     Right,
 | |
|     Both
 | |
| };
 | |
| 
 | |
| enum class TrimWhitespace {
 | |
|     Yes,
 | |
|     No,
 | |
| };
 | |
| 
 | |
| enum class SplitBehavior : unsigned {
 | |
|     // Neither keep empty substrings nor keep the trailing separator.
 | |
|     // This is the default behavior if unspecified.
 | |
|     Nothing = 0,
 | |
| 
 | |
|     // If two separators follow each other without any characters
 | |
|     // in between, keep a "" in the resulting vector. (or only the
 | |
|     // separator if KeepTrailingSeparator is used)
 | |
|     KeepEmpty = 1,
 | |
| 
 | |
|     // Do not strip off the separator at the end of the string.
 | |
|     KeepTrailingSeparator = 2,
 | |
| };
 | |
| AK_ENUM_BITWISE_OPERATORS(SplitBehavior);
 | |
| 
 | |
| struct MaskSpan {
 | |
|     size_t start;
 | |
|     size_t length;
 | |
| 
 | |
|     bool operator==(MaskSpan const& other) const
 | |
|     {
 | |
|         return start == other.start && length == other.length;
 | |
|     }
 | |
| };
 | |
| 
 | |
| namespace StringUtils {
 | |
| 
 | |
| bool matches(StringView str, StringView mask, CaseSensitivity = CaseSensitivity::CaseInsensitive, Vector<MaskSpan>* match_spans = nullptr);
 | |
| template<typename T = int>
 | |
| Optional<T> convert_to_int(StringView, TrimWhitespace = TrimWhitespace::Yes);
 | |
| template<typename T = unsigned>
 | |
| Optional<T> convert_to_uint(StringView, TrimWhitespace = TrimWhitespace::Yes);
 | |
| template<typename T = unsigned>
 | |
| Optional<T> convert_to_uint_from_hex(StringView, TrimWhitespace = TrimWhitespace::Yes);
 | |
| template<typename T = unsigned>
 | |
| Optional<T> convert_to_uint_from_octal(StringView, TrimWhitespace = TrimWhitespace::Yes);
 | |
| #ifndef KERNEL
 | |
| template<typename T>
 | |
| Optional<T> convert_to_floating_point(StringView, TrimWhitespace = TrimWhitespace::Yes);
 | |
| #endif
 | |
| bool equals_ignoring_case(StringView, StringView);
 | |
| bool ends_with(StringView a, StringView b, CaseSensitivity);
 | |
| bool starts_with(StringView, StringView, CaseSensitivity);
 | |
| bool contains(StringView, StringView, CaseSensitivity);
 | |
| bool is_whitespace(StringView);
 | |
| StringView trim(StringView string, StringView characters, TrimMode mode);
 | |
| StringView trim_whitespace(StringView string, TrimMode mode);
 | |
| 
 | |
| Optional<size_t> find(StringView haystack, char needle, size_t start = 0);
 | |
| Optional<size_t> find(StringView haystack, StringView needle, size_t start = 0);
 | |
| Optional<size_t> find_last(StringView haystack, char needle);
 | |
| Optional<size_t> find_last_not(StringView haystack, char needle);
 | |
| Vector<size_t> find_all(StringView haystack, StringView needle);
 | |
| enum class SearchDirection {
 | |
|     Forward,
 | |
|     Backward
 | |
| };
 | |
| Optional<size_t> find_any_of(StringView haystack, StringView needles, SearchDirection);
 | |
| 
 | |
| DeprecatedString to_snakecase(StringView);
 | |
| DeprecatedString to_titlecase(StringView);
 | |
| DeprecatedString invert_case(StringView);
 | |
| 
 | |
| DeprecatedString replace(StringView, StringView needle, StringView replacement, ReplaceMode);
 | |
| ErrorOr<String> replace(String const&, StringView needle, StringView replacement, ReplaceMode);
 | |
| 
 | |
| size_t count(StringView, StringView needle);
 | |
| 
 | |
| }
 | |
| 
 | |
| }
 | |
| 
 | |
| #if USING_AK_GLOBALLY
 | |
| using AK::CaseSensitivity;
 | |
| using AK::ReplaceMode;
 | |
| using AK::SplitBehavior;
 | |
| using AK::TrimMode;
 | |
| using AK::TrimWhitespace;
 | |
| #endif
 |