Skip to content

Commit

Permalink
AK+Everywhere: Turn bool keep_empty to an enum in split* functions
Browse files Browse the repository at this point in the history
  • Loading branch information
demostanis authored and linusg committed Oct 24, 2022
1 parent f485db2 commit 3e8b5ac
Show file tree
Hide file tree
Showing 44 changed files with 96 additions and 81 deletions.
2 changes: 1 addition & 1 deletion AK/IPv6Address.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ class [[gnu::packed]] IPv6Address {
if (string.is_null())
return {};

auto const parts = string.split_view(':', true);
auto const parts = string.split_view(':', SplitBehavior::KeepEmpty);
if (parts.is_empty())
return {};
if (parts.size() > 9) {
Expand Down
14 changes: 8 additions & 6 deletions AK/String.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,18 +97,19 @@ StringView String::substring_view(size_t start) const
return { characters() + start, length() - start };
}

Vector<String> String::split(char separator, bool keep_empty) const
Vector<String> String::split(char separator, SplitBehavior split_behavior) const
{
return split_limit(separator, 0, keep_empty);
return split_limit(separator, 0, split_behavior);
}

Vector<String> String::split_limit(char separator, size_t limit, bool keep_empty) const
Vector<String> String::split_limit(char separator, size_t limit, SplitBehavior split_behavior) const
{
if (is_empty())
return {};

Vector<String> v;
size_t substart = 0;
bool keep_empty = has_flag(split_behavior, SplitBehavior::KeepEmpty);
for (size_t i = 0; i < length() && (v.size() + 1) != limit; ++i) {
char ch = characters()[i];
if (ch == separator) {
Expand All @@ -124,13 +125,14 @@ Vector<String> String::split_limit(char separator, size_t limit, bool keep_empty
return v;
}

Vector<StringView> String::split_view(Function<bool(char)> separator, bool keep_empty) const
Vector<StringView> String::split_view(Function<bool(char)> separator, SplitBehavior split_behavior) const
{
if (is_empty())
return {};

Vector<StringView> v;
size_t substart = 0;
bool keep_empty = has_flag(split_behavior, SplitBehavior::KeepEmpty);
for (size_t i = 0; i < length(); ++i) {
char ch = characters()[i];
if (separator(ch)) {
Expand All @@ -146,9 +148,9 @@ Vector<StringView> String::split_view(Function<bool(char)> separator, bool keep_
return v;
}

Vector<StringView> String::split_view(char const separator, bool keep_empty) const
Vector<StringView> String::split_view(char const separator, SplitBehavior split_behavior) const
{
return split_view([separator](char ch) { return ch == separator; }, keep_empty);
return split_view([separator](char ch) { return ch == separator; }, split_behavior);
}

ByteBuffer String::to_byte_buffer() const
Expand Down
8 changes: 4 additions & 4 deletions AK/String.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,10 +150,10 @@ class String {
[[nodiscard]] bool contains(StringView, CaseSensitivity = CaseSensitivity::CaseSensitive) const;
[[nodiscard]] bool contains(char, CaseSensitivity = CaseSensitivity::CaseSensitive) const;

[[nodiscard]] Vector<String> split_limit(char separator, size_t limit, bool keep_empty = false) const;
[[nodiscard]] Vector<String> split(char separator, bool keep_empty = false) const;
[[nodiscard]] Vector<StringView> split_view(char separator, bool keep_empty = false) const;
[[nodiscard]] Vector<StringView> split_view(Function<bool(char)> separator, bool keep_empty = false) const;
[[nodiscard]] Vector<String> split_limit(char separator, size_t limit, SplitBehavior = SplitBehavior::Nothing) const;
[[nodiscard]] Vector<String> split(char separator, SplitBehavior = SplitBehavior::Nothing) const;
[[nodiscard]] Vector<StringView> split_view(char separator, SplitBehavior = SplitBehavior::Nothing) const;
[[nodiscard]] Vector<StringView> split_view(Function<bool(char)> separator, SplitBehavior = SplitBehavior::Nothing) const;

[[nodiscard]] Optional<size_t> find(char needle, size_t start = 0) const { return StringUtils::find(*this, needle, start); }
[[nodiscard]] Optional<size_t> find(StringView needle, size_t start = 0) const { return StringUtils::find(*this, needle, start); }
Expand Down
10 changes: 10 additions & 0 deletions AK/StringUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#pragma once

#include <AK/Concepts.h>
#include <AK/EnumBits.h>
#include <AK/Forward.h>

namespace AK {
Expand Down Expand Up @@ -38,6 +39,14 @@ enum class TrimWhitespace {
No,
};

enum class SplitBehavior : unsigned {
Nothing = 0,
// If two separators follow each other without any characters
// in between, keep a "" in the resulting vector.
KeepEmpty = 1,
};
AK_ENUM_BITWISE_OPERATORS(SplitBehavior);

struct MaskSpan {
size_t start;
size_t length;
Expand Down Expand Up @@ -99,5 +108,6 @@ size_t count(StringView, StringView needle);

using AK::CaseSensitivity;
using AK::ReplaceMode;
using AK::SplitBehavior;
using AK::TrimMode;
using AK::TrimWhitespace;
13 changes: 7 additions & 6 deletions AK/StringView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,16 @@ StringView::StringView(ByteBuffer const& buffer)
{
}

Vector<StringView> StringView::split_view(char const separator, bool keep_empty) const
Vector<StringView> StringView::split_view(char const separator, SplitBehavior split_behavior) const
{
StringView seperator_view { &separator, 1 };
return split_view(seperator_view, keep_empty);
return split_view(seperator_view, split_behavior);
}

Vector<StringView> StringView::split_view(StringView separator, bool keep_empty) const
Vector<StringView> StringView::split_view(StringView separator, SplitBehavior split_behavior) const
{
Vector<StringView> parts;
for_each_split_view(separator, keep_empty, [&](StringView view) {
for_each_split_view(separator, split_behavior, [&](StringView view) {
parts.append(view);
});
return parts;
Expand All @@ -61,7 +61,7 @@ Vector<StringView> StringView::lines(bool consider_cr) const
return {};

if (!consider_cr)
return split_view('\n', true);
return split_view('\n', SplitBehavior::KeepEmpty);

Vector<StringView> v;
size_t substart = 0;
Expand Down Expand Up @@ -264,13 +264,14 @@ Vector<size_t> StringView::find_all(StringView needle) const
return StringUtils::find_all(*this, needle);
}

Vector<StringView> StringView::split_view_if(Function<bool(char)> const& predicate, bool keep_empty) const
Vector<StringView> StringView::split_view_if(Function<bool(char)> const& predicate, SplitBehavior split_behavior) const
{
if (is_empty())
return {};

Vector<StringView> v;
size_t substart = 0;
bool keep_empty = has_flag(split_behavior, SplitBehavior::KeepEmpty);
for (size_t i = 0; i < length(); ++i) {
char ch = characters_without_null_termination()[i];
if (predicate(ch)) {
Expand Down
14 changes: 8 additions & 6 deletions AK/StringView.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include <AK/Assertions.h>
#include <AK/Checked.h>
#include <AK/EnumBits.h>
#include <AK/Forward.h>
#include <AK/Optional.h>
#include <AK/Span.h>
Expand Down Expand Up @@ -129,10 +130,10 @@ class StringView {
return substring_view(start, length() - start);
}

[[nodiscard]] Vector<StringView> split_view(char, bool keep_empty = false) const;
[[nodiscard]] Vector<StringView> split_view(StringView, bool keep_empty = false) const;
[[nodiscard]] Vector<StringView> split_view(char, SplitBehavior = SplitBehavior::Nothing) const;
[[nodiscard]] Vector<StringView> split_view(StringView, SplitBehavior = SplitBehavior::Nothing) const;

[[nodiscard]] Vector<StringView> split_view_if(Function<bool(char)> const& predicate, bool keep_empty = false) const;
[[nodiscard]] Vector<StringView> split_view_if(Function<bool(char)> const& predicate, SplitBehavior = SplitBehavior::Nothing) const;

[[nodiscard]] StringView find_last_split_view(char separator) const
{
Expand All @@ -151,14 +152,14 @@ class StringView {
}

template<VoidFunction<StringView> Callback>
void for_each_split_view(char separator, bool keep_empty, Callback callback) const
void for_each_split_view(char separator, SplitBehavior split_behavior, Callback callback) const
{
StringView seperator_view { &separator, 1 };
for_each_split_view(seperator_view, keep_empty, callback);
for_each_split_view(seperator_view, split_behavior, callback);
}

template<VoidFunction<StringView> Callback>
void for_each_split_view(StringView separator, bool keep_empty, Callback callback) const
void for_each_split_view(StringView separator, SplitBehavior split_behavior, Callback callback) const
{
VERIFY(!separator.is_empty());

Expand All @@ -168,6 +169,7 @@ class StringView {
StringView view { *this };

auto maybe_separator_index = find(separator);
bool keep_empty = has_flag(split_behavior, SplitBehavior::KeepEmpty);
while (maybe_separator_index.has_value()) {
auto separator_index = maybe_separator_index.value();
auto part_with_separator = view.substring_view(0, separator_index + separator.length());
Expand Down
2 changes: 1 addition & 1 deletion Kernel/CommandLine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ UNMAP_AFTER_INIT void CommandLine::add_arguments(Vector<StringView> const& args)
continue;
}

auto pair = str.split_view('=', false);
auto pair = str.split_view('=');
VERIFY(pair.size() == 2 || pair.size() == 1);

if (pair.size() == 1) {
Expand Down
2 changes: 1 addition & 1 deletion Kernel/GlobalProcessExposed.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -642,7 +642,7 @@ class ProcFSCPUInformation final : public ProcFSGlobalInformation {
TRY(obj.add("hypervisor_vendor_id"sv, info.hypervisor_vendor_id_string()));

auto features_array = TRY(obj.add_array("features"sv));
auto keep_empty = false;
auto keep_empty = SplitBehavior::KeepEmpty;

ErrorOr<void> result; // FIXME: Make this nicer
info.features_string().for_each_split_view(' ', keep_empty, [&](StringView feature) {
Expand Down
2 changes: 1 addition & 1 deletion Kernel/Storage/StorageManagement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ UNMAP_AFTER_INIT Array<unsigned, 3> StorageManagement::extract_boot_device_addre
auto parameters_view = m_boot_argument.substring_view(device_prefix.length()).find_first_split_view(';');
size_t parts_count = 0;
bool parse_failure = false;
parameters_view.for_each_split_view(':', false, [&](StringView parameter_view) {
parameters_view.for_each_split_view(':', SplitBehavior::Nothing, [&](StringView parameter_view) {
if (parse_failure)
return;
if (parts_count > 2)
Expand Down
2 changes: 1 addition & 1 deletion Kernel/Syscalls/pledge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ ErrorOr<FlatPtr> Process::sys$pledge(Userspace<Syscall::SC_pledge_params const*>

auto parse_pledge = [&](auto pledge_spec, u32& mask) {
auto found_invalid_pledge = true;
pledge_spec.for_each_split_view(' ', false, [&mask, &found_invalid_pledge](auto const& part) {
pledge_spec.for_each_split_view(' ', SplitBehavior::Nothing, [&mask, &found_invalid_pledge](auto const& part) {
#define __ENUMERATE_PLEDGE_PROMISE(x) \
if (part == #x##sv) { \
mask |= (1u << (u32)Pledge::x); \
Expand Down
2 changes: 1 addition & 1 deletion Meta/Lagom/Tools/CodeGenerators/LibEDID/GeneratePnpIDs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ static ErrorOr<String> decode_html_entities(StringView const& str)

static ErrorOr<ApprovalDate> parse_approval_date(StringView const& str)
{
auto parts = str.trim_whitespace().split_view('/', true);
auto parts = str.trim_whitespace().split_view('/', SplitBehavior::KeepEmpty);
if (parts.size() != 3)
return Error::from_string_literal("Failed to parse approval date parts (mm/dd/yyyy)");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ static Relation parse_relation(StringView relation)
parsed.symbol = lhs[0];
}

rhs.for_each_split_view(set_operator, false, [&](auto set) {
rhs.for_each_split_view(set_operator, SplitBehavior::Nothing, [&](auto set) {
if (auto index = set.find(range_operator); index.has_value()) {
auto range_begin = set.substring_view(0, *index).to_uint();
VERIFY(range_begin.has_value());
Expand Down Expand Up @@ -313,10 +313,10 @@ static void parse_condition(StringView category, StringView rule, Conditions& ru
// and_condition = relation ('and' relation)*
//
// This affords some simplicity in that disjunctions are never embedded within a conjunction.
condition.for_each_split_view(disjunction_keyword, false, [&](auto disjunction) {
condition.for_each_split_view(disjunction_keyword, SplitBehavior::Nothing, [&](auto disjunction) {
Vector<Relation> conjunctions;

disjunction.for_each_split_view(conjunction_keyword, false, [&](auto relation) {
disjunction.for_each_split_view(conjunction_keyword, SplitBehavior::Nothing, [&](auto relation) {
conjunctions.append(parse_relation(relation));
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ static ErrorOr<void> parse_time_zone_coordinates(Core::Stream::BufferedFile& fil

time_zone_data.time_zone_coordinates.set(zone, { latitude, longitude });

regions.for_each_split_view(',', false, [&](auto region) {
regions.for_each_split_view(',', SplitBehavior::Nothing, [&](auto region) {
auto index = time_zone_data.unique_strings.ensure(zone);
time_zone_data.time_zone_regions.ensure(region).append(index);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ static ErrorOr<void> parse_emoji_serenity_data(Core::Stream::BufferedFile& file,
emoji.group = Unicode::EmojiGroup::SerenityOS;
emoji.display_order = display_order++;

line.for_each_split_view(' ', false, [&](auto segment) {
line.for_each_split_view(' ', SplitBehavior::Nothing, [&](auto segment) {
if (segment.starts_with(code_point_header)) {
segment = segment.substring_view(code_point_header.length());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ static ErrorOr<void> parse_special_casing(Core::Stream::BufferedFile& file, Unic
if (auto index = line.find('#'); index.has_value())
line = line.substring_view(0, *index);

auto segments = line.split_view(';', true);
auto segments = line.split_view(';', SplitBehavior::KeepEmpty);
VERIFY(segments.size() == 5 || segments.size() == 6);

SpecialCasing casing {};
Expand All @@ -233,7 +233,7 @@ static ErrorOr<void> parse_special_casing(Core::Stream::BufferedFile& file, Unic
casing.uppercase_mapping = parse_code_point_list(segments[3]);

if (auto condition = segments[4].trim_whitespace(); !condition.is_empty()) {
auto conditions = condition.split_view(' ', true);
auto conditions = condition.split_view(' ', SplitBehavior::KeepEmpty);
VERIFY(conditions.size() == 1 || conditions.size() == 2);

if (conditions.size() == 2) {
Expand Down Expand Up @@ -294,7 +294,7 @@ static ErrorOr<void> parse_prop_list(Core::Stream::BufferedFile& file, PropList&
if (auto index = line.find('#'); index.has_value())
line = line.substring_view(0, *index);

auto segments = line.split_view(';', true);
auto segments = line.split_view(';', SplitBehavior::KeepEmpty);
VERIFY(segments.size() == 2);

auto code_point_range = parse_code_point_range(segments[0].trim_whitespace());
Expand Down Expand Up @@ -344,7 +344,7 @@ static ErrorOr<void> parse_alias_list(Core::Stream::BufferedFile& file, PropList
if (current_property != "Binary Properties"sv)
continue;

auto segments = line.split_view(';', true);
auto segments = line.split_view(';', SplitBehavior::KeepEmpty);
VERIFY((segments.size() == 2) || (segments.size() == 3));

auto alias = segments[0].trim_whitespace();
Expand All @@ -370,7 +370,7 @@ static ErrorOr<void> parse_name_aliases(Core::Stream::BufferedFile& file, Unicod
if (line.is_empty() || line.starts_with('#'))
continue;

auto segments = line.split_view(';', true);
auto segments = line.split_view(';', SplitBehavior::KeepEmpty);
VERIFY(segments.size() == 3);

auto code_point = AK::StringUtils::convert_to_uint_from_hex<u32>(segments[0].trim_whitespace());
Expand Down Expand Up @@ -417,7 +417,7 @@ static ErrorOr<void> parse_value_alias_list(Core::Stream::BufferedFile& file, St
if (auto index = line.find('#'); index.has_value())
line = line.substring_view(0, *index);

auto segments = line.split_view(';', true);
auto segments = line.split_view(';', SplitBehavior::KeepEmpty);
auto category = segments[0].trim_whitespace();

if (category != desired_category)
Expand Down Expand Up @@ -450,7 +450,7 @@ static ErrorOr<void> parse_normalization_props(Core::Stream::BufferedFile& file,
if (auto index = line.find('#'); index.has_value())
line = line.substring_view(0, *index);

auto segments = line.split_view(';', true);
auto segments = line.split_view(';', SplitBehavior::KeepEmpty);
VERIFY((segments.size() == 2) || (segments.size() == 3));

auto code_point_range = parse_code_point_range(segments[0].trim_whitespace());
Expand Down Expand Up @@ -579,7 +579,7 @@ static ErrorOr<void> parse_block_display_names(Core::Stream::BufferedFile& file,
if (line.is_empty() || line.starts_with('#'))
continue;

auto segments = line.split_view(';', true);
auto segments = line.split_view(';', SplitBehavior::KeepEmpty);
VERIFY(segments.size() == 2);

auto code_point_range = parse_code_point_range(segments[0].trim_whitespace());
Expand Down Expand Up @@ -610,7 +610,7 @@ static ErrorOr<void> parse_unicode_data(Core::Stream::BufferedFile& file, Unicod
if (line.is_empty())
continue;

auto segments = line.split_view(';', true);
auto segments = line.split_view(';', SplitBehavior::KeepEmpty);
VERIFY(segments.size() == 15);

CodePointData data {};
Expand Down
2 changes: 1 addition & 1 deletion Meta/Lagom/Tools/ConfigureComponents/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ static Result<Vector<String>, int> run_whiptail(WhiptailMode mode, Vector<Whipta
auto file = Core::File::construct();
file->open(read_fd, Core::OpenMode::ReadOnly, Core::File::ShouldCloseFileDescriptor::Yes);
auto data = String::copy(file->read_all());
return data.split('\n', false);
return data.split('\n');
}

static bool run_system_command(String const& command, StringView command_name)
Expand Down
6 changes: 3 additions & 3 deletions Tests/AK/TestString.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ TEST_CASE(split)
EXPECT_EQ(parts[0], "a");
EXPECT_EQ(parts[1], "b");

parts = test.split(' ', true);
parts = test.split(' ', SplitBehavior::KeepEmpty);
EXPECT_EQ(parts.size(), 5u);
EXPECT_EQ(parts[0], "a");
EXPECT_EQ(parts[1], "");
Expand All @@ -243,9 +243,9 @@ TEST_CASE(split)

test = "axxbx";
EXPECT_EQ(test.split('x').size(), 2u);
EXPECT_EQ(test.split('x', true).size(), 4u);
EXPECT_EQ(test.split('x', SplitBehavior::KeepEmpty).size(), 4u);
EXPECT_EQ(test.split_view('x').size(), 2u);
EXPECT_EQ(test.split_view('x', true).size(), 4u);
EXPECT_EQ(test.split_view('x', SplitBehavior::KeepEmpty).size(), 4u);
}

TEST_CASE(builder_zero_initial_capacity)
Expand Down
Loading

0 comments on commit 3e8b5ac

Please sign in to comment.