LibUnicode: Generate enum/alias from-string methods without a HashMap

The *_from_string() and resolve_*_alias() generated methods are the last
remaining users of HashMap in the LibUnicode generated files (read: the
last methods not using compile-time structures). This converts these
methods to use an array containing pairs of hash values to the desired
lookup value.

Because this code generation is the same between GenerateUnicodeData.cpp
and GenerateUnicodeLocale.cpp, this adds a GeneratorUtil.h header to the
LibUnicode generators to contain the method that generates the methods.
This commit is contained in:
Timothy Flynn 2021-10-12 12:22:47 -04:00 committed by Andreas Kling
parent 203ee58aa2
commit f91d63af83
Notes: sideshowbarker 2024-07-18 02:47:08 +09:00
3 changed files with 120 additions and 80 deletions

View file

@ -4,6 +4,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "GeneratorUtil.h"
#include <AK/AllOf.h>
#include <AK/Array.h>
#include <AK/CharacterTypes.h>
@ -560,7 +561,6 @@ static void generate_unicode_data_implementation(Core::File& file, UnicodeData c
#include <AK/Array.h>
#include <AK/BinarySearch.h>
#include <AK/CharacterTypes.h>
#include <AK/HashMap.h>
#include <AK/String.h>
#include <AK/StringView.h>
#include <LibUnicode/UnicodeData.h>
@ -810,33 +810,15 @@ bool code_point_has_@enum_snake@(u32 code_point, @enum_title@ @enum_snake@)
};
auto append_from_string = [&](StringView enum_title, StringView enum_snake, PropList const& prop_list, Vector<Alias> const& aliases) {
generator.set("enum_title", enum_title);
generator.set("enum_snake", enum_snake);
HashValueMap<StringView> hashes;
hashes.ensure_capacity(prop_list.size() + aliases.size());
auto properties = prop_list.keys();
for (auto const& prop : prop_list)
hashes.set(prop.key.hash(), prop.key);
for (auto const& alias : aliases)
properties.append(alias.alias);
quick_sort(properties);
hashes.set(alias.alias.hash(), alias.alias);
generator.append(R"~~~(
Optional<@enum_title@> @enum_snake@_from_string(StringView const& @enum_snake@)
{
static HashMap<String, @enum_title@> @enum_snake@_values { {)~~~");
for (auto const& property : properties) {
generator.set("property", property);
generator.append(R"~~~(
{ "@property@"sv, @enum_title@::@property@ },)~~~");
}
generator.append(R"~~~(
} };
if (auto value = @enum_snake@_values.get(@enum_snake@); value.has_value())
return value.value();
return {};
}
)~~~");
generate_value_from_string(generator, "{}_from_string"sv, enum_title, enum_snake, move(hashes));
};
append_prop_search("GeneralCategory"sv, "general_category"sv, "s_general_categories"sv);

View file

@ -4,6 +4,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "GeneratorUtil.h"
#include <AK/AllOf.h>
#include <AK/CharacterTypes.h>
#include <AK/Format.h>
@ -666,7 +667,7 @@ static void generate_unicode_locale_implementation(Core::File& file, UnicodeLoca
generator.append(R"~~~(
#include <AK/Array.h>
#include <AK/HashMap.h>
#include <AK/BinarySearch.h>
#include <AK/Span.h>
#include <LibUnicode/Locale.h>
#include <LibUnicode/UnicodeLocale.h>
@ -1043,67 +1044,24 @@ Optional<StringView> get_locale_@enum_snake@_mapping(StringView locale, StringVi
)~~~");
};
auto append_from_string = [&](StringView enum_title, StringView enum_snake, Vector<String> const& values) {
generator.set("enum_title", enum_title);
generator.set("enum_snake", enum_snake);
auto append_from_string = [&](StringView enum_title, StringView enum_snake, auto const& values) {
HashValueMap<String> hashes;
hashes.ensure_capacity(values.size());
generator.append(R"~~~(
Optional<@enum_title@> @enum_snake@_from_string(StringView const& @enum_snake@)
{
static HashMap<StringView, @enum_title@> @enum_snake@_values { {)~~~");
for (auto const& value : values)
hashes.set(value.hash(), format_identifier(enum_title, value));
for (auto const& value : values) {
generator.set("key"sv, value);
generator.set("value"sv, format_identifier(enum_title, value));
generator.append(R"~~~(
{ "@key@"sv, @enum_title@::@value@ },)~~~");
}
generator.append(R"~~~(
} };
if (auto value = @enum_snake@_values.get(@enum_snake@); value.has_value())
return value.value();
return {};
}
)~~~");
generate_value_from_string(generator, "{}_from_string"sv, enum_title, enum_snake, move(hashes));
};
auto append_alias_search = [&](StringView enum_snake, auto const& aliases) {
generator.set("enum_snake", enum_snake);
HashValueMap<size_t> hashes;
hashes.ensure_capacity(aliases.size());
generator.append(R"~~~(
Optional<StringView> resolve_@enum_snake@_alias(StringView const& @enum_snake@)
{
static HashMap<StringView, size_t> @enum_snake@_aliases { {
)~~~");
for (auto const& alias : aliases)
hashes.set(alias.key.hash(), alias.value);
constexpr size_t max_values_per_row = 10;
size_t values_in_current_row = 0;
for (auto const& alias : aliases) {
if (values_in_current_row++ > 0)
generator.append(" ");
generator.set("key"sv, alias.key);
generator.set("alias"sv, String::number(alias.value));
generator.append("{ \"@key@\"sv, @alias@ },");
if (values_in_current_row == max_values_per_row) {
generator.append("\n ");
values_in_current_row = 0;
}
}
generator.append(R"~~~(
} };
if (auto alias = @enum_snake@_aliases.get(@enum_snake@); alias.has_value())
return s_string_list[alias.value()];
return {};
}
)~~~");
generate_value_from_string(generator, "resolve_{}_alias"sv, "size_t"sv, enum_snake, move(hashes), "StringView"sv, "s_string_list[{}]"sv);
};
append_from_string("Locale"sv, "locale"sv, locale_data.locales.keys());

View file

@ -0,0 +1,100 @@
/*
* Copyright (c) 2021, Tim Flynn <trflynn89@pm.me>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/HashMap.h>
#include <AK/Optional.h>
#include <AK/QuickSort.h>
#include <AK/SourceGenerator.h>
#include <AK/String.h>
#include <AK/StringView.h>
#include <AK/Vector.h>
inline void ensure_from_string_types_are_generated(SourceGenerator& generator)
{
static bool generated_from_string_types = false;
if (generated_from_string_types)
return;
generator.append(R"~~~(
template <typename ValueType>
struct HashValuePair {
unsigned hash { 0 };
ValueType value {};
};
template <typename ValueType>
struct HashValueComparator
{
constexpr int operator()(unsigned hash, HashValuePair<ValueType> const& pair)
{
if (hash > pair.hash)
return 1;
if (hash < pair.hash)
return -1;
return 0;
}
};
)~~~");
generated_from_string_types = true;
}
template<typename ValueType>
using HashValueMap = HashMap<unsigned, ValueType>;
template<typename ValueType>
void generate_value_from_string(SourceGenerator& generator, StringView method_name_format, StringView value_type, StringView value_name, HashValueMap<ValueType> hashes, Optional<StringView> return_type = {}, StringView return_format = "{}"sv)
{
ensure_from_string_types_are_generated(generator);
generator.set("method_name", String::formatted(method_name_format, value_name));
generator.set("value_type", value_type);
generator.set("value_name", value_name);
generator.set("return_type", return_type.has_value() ? *return_type : value_type);
generator.set("size", String::number(hashes.size()));
generator.append(R"~~~(
Optional<@return_type@> @method_name@(StringView const& key)
{
constexpr Array<HashValuePair<@value_type@>, @size@> hash_pairs { {
)~~~");
auto hash_keys = hashes.keys();
quick_sort(hash_keys);
constexpr size_t max_values_per_row = 10;
size_t values_in_current_row = 0;
for (auto hash_key : hash_keys) {
if (values_in_current_row++ > 0)
generator.append(" ");
if constexpr (IsIntegral<ValueType>)
generator.set("value"sv, String::number(hashes.get(hash_key).value()));
else
generator.set("value"sv, String::formatted("{}::{}", value_type, hashes.get(hash_key).value()));
generator.set("hash"sv, String::number(hash_key));
generator.append("{ @hash@U, @value@ },"sv);
if (values_in_current_row == max_values_per_row) {
generator.append("\n ");
values_in_current_row = 0;
}
}
generator.set("return_statement", String::formatted(return_format, "value->value"sv));
generator.append(R"~~~(
} };
if (auto const* value = binary_search(hash_pairs, key.hash(), nullptr, HashValueComparator<@value_type@> {}))
return @return_statement@;
return {};
}
)~~~");
}