Serenity Operating System
at master 232 lines 5.4 kB view raw
1/* 2 * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <AK/ByteBuffer.h> 8#include <AK/Checked.h> 9#include <AK/PrintfImplementation.h> 10#include <AK/String.h> 11#include <AK/StringBuilder.h> 12#include <AK/StringView.h> 13#include <AK/UnicodeUtils.h> 14#include <AK/Utf32View.h> 15 16#ifndef KERNEL 17# include <AK/DeprecatedString.h> 18# include <AK/FlyString.h> 19# include <AK/Utf16View.h> 20#endif 21 22namespace AK { 23 24inline ErrorOr<void> StringBuilder::will_append(size_t size) 25{ 26 Checked<size_t> needed_capacity = m_buffer.size(); 27 needed_capacity += size; 28 VERIFY(!needed_capacity.has_overflow()); 29 // Prefer to completely use the existing capacity first 30 if (needed_capacity <= m_buffer.capacity()) 31 return {}; 32 Checked<size_t> expanded_capacity = needed_capacity; 33 expanded_capacity *= 2; 34 VERIFY(!expanded_capacity.has_overflow()); 35 TRY(m_buffer.try_ensure_capacity(expanded_capacity.value())); 36 return {}; 37} 38 39ErrorOr<StringBuilder> StringBuilder::create(size_t initial_capacity) 40{ 41 StringBuilder builder; 42 TRY(builder.m_buffer.try_ensure_capacity(initial_capacity)); 43 return builder; 44} 45 46StringBuilder::StringBuilder(size_t initial_capacity) 47{ 48 m_buffer.ensure_capacity(initial_capacity); 49} 50 51ErrorOr<void> StringBuilder::try_append(StringView string) 52{ 53 if (string.is_empty()) 54 return {}; 55 TRY(will_append(string.length())); 56 TRY(m_buffer.try_append(string.characters_without_null_termination(), string.length())); 57 return {}; 58} 59 60ErrorOr<void> StringBuilder::try_append(char ch) 61{ 62 TRY(will_append(1)); 63 TRY(m_buffer.try_append(ch)); 64 return {}; 65} 66 67ErrorOr<void> StringBuilder::try_append_repeated(char ch, size_t n) 68{ 69 TRY(will_append(n)); 70 for (size_t i = 0; i < n; ++i) 71 TRY(try_append(ch)); 72 return {}; 73} 74 75void StringBuilder::append(StringView string) 76{ 77 MUST(try_append(string)); 78} 79 80ErrorOr<void> StringBuilder::try_append(char const* characters, size_t length) 81{ 82 return try_append(StringView { characters, length }); 83} 84 85void StringBuilder::append(char const* characters, size_t length) 86{ 87 MUST(try_append(characters, length)); 88} 89 90void StringBuilder::append(char ch) 91{ 92 MUST(try_append(ch)); 93} 94 95void StringBuilder::appendvf(char const* fmt, va_list ap) 96{ 97 printf_internal([this](char*&, char ch) { 98 append(ch); 99 }, 100 nullptr, fmt, ap); 101} 102 103void StringBuilder::append_repeated(char ch, size_t n) 104{ 105 MUST(try_append_repeated(ch, n)); 106} 107 108ErrorOr<ByteBuffer> StringBuilder::to_byte_buffer() const 109{ 110 return ByteBuffer::copy(data(), length()); 111} 112 113#ifndef KERNEL 114DeprecatedString StringBuilder::to_deprecated_string() const 115{ 116 if (is_empty()) 117 return DeprecatedString::empty(); 118 return DeprecatedString((char const*)data(), length()); 119} 120 121ErrorOr<String> StringBuilder::to_string() const 122{ 123 return String::from_utf8(string_view()); 124} 125 126ErrorOr<FlyString> StringBuilder::to_fly_string() const 127{ 128 return FlyString::from_utf8(string_view()); 129} 130#endif 131 132StringView StringBuilder::string_view() const 133{ 134 return StringView { data(), m_buffer.size() }; 135} 136 137void StringBuilder::clear() 138{ 139 m_buffer.clear(); 140} 141 142ErrorOr<void> StringBuilder::try_append_code_point(u32 code_point) 143{ 144 auto nwritten = TRY(AK::UnicodeUtils::try_code_point_to_utf8(code_point, [this](char c) { return try_append(c); })); 145 if (nwritten < 0) { 146 TRY(try_append(0xef)); 147 TRY(try_append(0xbf)); 148 TRY(try_append(0xbd)); 149 } 150 return {}; 151} 152 153void StringBuilder::append_code_point(u32 code_point) 154{ 155 MUST(try_append_code_point(code_point)); 156} 157 158#ifndef KERNEL 159ErrorOr<void> StringBuilder::try_append(Utf16View const& utf16_view) 160{ 161 for (size_t i = 0; i < utf16_view.length_in_code_units();) { 162 auto code_point = utf16_view.code_point_at(i); 163 TRY(try_append_code_point(code_point)); 164 165 i += (code_point > 0xffff ? 2 : 1); 166 } 167 return {}; 168} 169 170void StringBuilder::append(Utf16View const& utf16_view) 171{ 172 MUST(try_append(utf16_view)); 173} 174#endif 175 176ErrorOr<void> StringBuilder::try_append(Utf32View const& utf32_view) 177{ 178 for (size_t i = 0; i < utf32_view.length(); ++i) { 179 auto code_point = utf32_view.code_points()[i]; 180 TRY(try_append_code_point(code_point)); 181 } 182 return {}; 183} 184 185void StringBuilder::append(Utf32View const& utf32_view) 186{ 187 MUST(try_append(utf32_view)); 188} 189 190void StringBuilder::append_as_lowercase(char ch) 191{ 192 if (ch >= 'A' && ch <= 'Z') 193 append(ch + 0x20); 194 else 195 append(ch); 196} 197 198void StringBuilder::append_escaped_for_json(StringView string) 199{ 200 MUST(try_append_escaped_for_json(string)); 201} 202 203ErrorOr<void> StringBuilder::try_append_escaped_for_json(StringView string) 204{ 205 for (auto ch : string) { 206 switch (ch) { 207 case '\b': 208 TRY(try_append("\\b"sv)); 209 break; 210 case '\n': 211 TRY(try_append("\\n"sv)); 212 break; 213 case '\t': 214 TRY(try_append("\\t"sv)); 215 break; 216 case '\"': 217 TRY(try_append("\\\""sv)); 218 break; 219 case '\\': 220 TRY(try_append("\\\\"sv)); 221 break; 222 default: 223 if (ch >= 0 && ch <= 0x1f) 224 TRY(try_appendff("\\u{:04x}", ch)); 225 else 226 TRY(try_append(ch)); 227 } 228 } 229 return {}; 230} 231 232}