mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 15:07:45 +00:00
AK: Implement string formatting for FixedStringBuffers
To ensure this happens without duplicating code, we allow forcing a StringBuilder object to only use the inline buffer, so the code in the AK/Format.cpp file doesn't need to deal with different underlying storage types (expandable or inline-fixed) at all.
This commit is contained in:
parent
b096f39e92
commit
b2fd51f561
3 changed files with 76 additions and 6 deletions
|
@ -7,6 +7,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <AK/Array.h>
|
#include <AK/Array.h>
|
||||||
|
#include <AK/StringBuilder.h>
|
||||||
#include <AK/StringView.h>
|
#include <AK/StringView.h>
|
||||||
#include <AK/TypedTransfer.h>
|
#include <AK/TypedTransfer.h>
|
||||||
#include <AK/Userspace.h>
|
#include <AK/Userspace.h>
|
||||||
|
@ -22,6 +23,24 @@ namespace AK {
|
||||||
template<size_t Size>
|
template<size_t Size>
|
||||||
class FixedStringBuffer {
|
class FixedStringBuffer {
|
||||||
public:
|
public:
|
||||||
|
[[nodiscard]] static ErrorOr<FixedStringBuffer<Size>> vformatted(StringView fmtstr, AK::TypeErasedFormatParams& params)
|
||||||
|
requires(Size < StringBuilder::inline_capacity)
|
||||||
|
{
|
||||||
|
StringBuilder builder { StringBuilder::UseInlineCapacityOnly::Yes };
|
||||||
|
TRY(AK::vformat(builder, fmtstr, params));
|
||||||
|
FixedStringBuffer<Size> buffer {};
|
||||||
|
buffer.store_characters(builder.string_view());
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Parameters>
|
||||||
|
[[nodiscard]] static ErrorOr<FixedStringBuffer<Size>> formatted(CheckedFormatString<Parameters...>&& fmtstr, Parameters const&... parameters)
|
||||||
|
requires(Size < StringBuilder::inline_capacity)
|
||||||
|
{
|
||||||
|
AK::VariadicFormatParams<AK::AllowDebugOnlyFormatters::No, Parameters...> variadic_format_parameters { parameters... };
|
||||||
|
return vformatted(fmtstr.view(), variadic_format_parameters);
|
||||||
|
}
|
||||||
|
|
||||||
void store_characters(StringView characters)
|
void store_characters(StringView characters)
|
||||||
{
|
{
|
||||||
// NOTE: Only store the characters up to the first null terminator
|
// NOTE: Only store the characters up to the first null terminator
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
|
* Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
|
||||||
|
* Copyright (c) 2023, Liav A. <liavalb@hotmail.co.il>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -23,6 +24,17 @@ namespace AK {
|
||||||
|
|
||||||
inline ErrorOr<void> StringBuilder::will_append(size_t size)
|
inline ErrorOr<void> StringBuilder::will_append(size_t size)
|
||||||
{
|
{
|
||||||
|
if (m_use_inline_capacity_only == UseInlineCapacityOnly::Yes) {
|
||||||
|
VERIFY(m_buffer.capacity() == StringBuilder::inline_capacity);
|
||||||
|
Checked<size_t> current_pointer = m_buffer.size();
|
||||||
|
current_pointer += size;
|
||||||
|
VERIFY(!current_pointer.has_overflow());
|
||||||
|
if (current_pointer <= StringBuilder::inline_capacity) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return Error::from_errno(ENOMEM);
|
||||||
|
}
|
||||||
|
|
||||||
Checked<size_t> needed_capacity = m_buffer.size();
|
Checked<size_t> needed_capacity = m_buffer.size();
|
||||||
needed_capacity += size;
|
needed_capacity += size;
|
||||||
VERIFY(!needed_capacity.has_overflow());
|
VERIFY(!needed_capacity.has_overflow());
|
||||||
|
@ -48,6 +60,27 @@ StringBuilder::StringBuilder(size_t initial_capacity)
|
||||||
m_buffer.ensure_capacity(initial_capacity);
|
m_buffer.ensure_capacity(initial_capacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StringBuilder::StringBuilder(UseInlineCapacityOnly use_inline_capacity_only)
|
||||||
|
: m_use_inline_capacity_only(use_inline_capacity_only)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t StringBuilder::length() const
|
||||||
|
{
|
||||||
|
return m_buffer.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StringBuilder::is_empty() const
|
||||||
|
{
|
||||||
|
return m_buffer.is_empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StringBuilder::trim(size_t count)
|
||||||
|
{
|
||||||
|
auto decrease_count = min(m_buffer.size(), count);
|
||||||
|
m_buffer.resize(m_buffer.size() - decrease_count);
|
||||||
|
}
|
||||||
|
|
||||||
ErrorOr<void> StringBuilder::try_append(StringView string)
|
ErrorOr<void> StringBuilder::try_append(StringView string)
|
||||||
{
|
{
|
||||||
if (string.is_empty())
|
if (string.is_empty())
|
||||||
|
@ -129,6 +162,16 @@ ErrorOr<FlyString> StringBuilder::to_fly_string() const
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
u8* StringBuilder::data()
|
||||||
|
{
|
||||||
|
return m_buffer.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 const* StringBuilder::data() const
|
||||||
|
{
|
||||||
|
return m_buffer.data();
|
||||||
|
}
|
||||||
|
|
||||||
StringView StringBuilder::string_view() const
|
StringView StringBuilder::string_view() const
|
||||||
{
|
{
|
||||||
return StringView { data(), m_buffer.size() };
|
return StringView { data(), m_buffer.size() };
|
||||||
|
|
|
@ -16,11 +16,19 @@ namespace AK {
|
||||||
|
|
||||||
class StringBuilder {
|
class StringBuilder {
|
||||||
public:
|
public:
|
||||||
|
static constexpr size_t inline_capacity = 256;
|
||||||
|
|
||||||
using OutputType = DeprecatedString;
|
using OutputType = DeprecatedString;
|
||||||
|
|
||||||
static ErrorOr<StringBuilder> create(size_t initial_capacity = inline_capacity);
|
static ErrorOr<StringBuilder> create(size_t initial_capacity = inline_capacity);
|
||||||
|
|
||||||
explicit StringBuilder(size_t initial_capacity = inline_capacity);
|
explicit StringBuilder(size_t initial_capacity = inline_capacity);
|
||||||
|
|
||||||
|
enum class UseInlineCapacityOnly {
|
||||||
|
Yes,
|
||||||
|
No,
|
||||||
|
};
|
||||||
|
explicit StringBuilder(UseInlineCapacityOnly use_inline_capacity_only);
|
||||||
~StringBuilder() = default;
|
~StringBuilder() = default;
|
||||||
|
|
||||||
ErrorOr<void> try_append(StringView);
|
ErrorOr<void> try_append(StringView);
|
||||||
|
@ -73,9 +81,9 @@ public:
|
||||||
[[nodiscard]] StringView string_view() const;
|
[[nodiscard]] StringView string_view() const;
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
[[nodiscard]] size_t length() const { return m_buffer.size(); }
|
[[nodiscard]] size_t length() const;
|
||||||
[[nodiscard]] bool is_empty() const { return m_buffer.is_empty(); }
|
[[nodiscard]] bool is_empty() const;
|
||||||
void trim(size_t count) { m_buffer.resize(m_buffer.size() - count); }
|
void trim(size_t count);
|
||||||
|
|
||||||
template<class SeparatorType, class CollectionType>
|
template<class SeparatorType, class CollectionType>
|
||||||
void join(SeparatorType const& separator, CollectionType const& collection, StringView fmtstr = "{}"sv)
|
void join(SeparatorType const& separator, CollectionType const& collection, StringView fmtstr = "{}"sv)
|
||||||
|
@ -98,10 +106,10 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ErrorOr<void> will_append(size_t);
|
ErrorOr<void> will_append(size_t);
|
||||||
u8* data() { return m_buffer.data(); }
|
u8* data();
|
||||||
u8 const* data() const { return m_buffer.data(); }
|
u8 const* data() const;
|
||||||
|
|
||||||
static constexpr size_t inline_capacity = 256;
|
UseInlineCapacityOnly m_use_inline_capacity_only { UseInlineCapacityOnly::No };
|
||||||
Detail::ByteBuffer<inline_capacity> m_buffer;
|
Detail::ByteBuffer<inline_capacity> m_buffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue