From f91d63af83a9760e5e3d606272d5708d1f87022f Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Tue, 12 Oct 2021 12:22:47 -0400 Subject: [PATCH] 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. --- .../LibUnicode/GenerateUnicodeData.cpp | 32 ++---- .../LibUnicode/GenerateUnicodeLocale.cpp | 68 +++--------- .../CodeGenerators/LibUnicode/GeneratorUtil.h | 100 ++++++++++++++++++ 3 files changed, 120 insertions(+), 80 deletions(-) create mode 100644 Meta/Lagom/Tools/CodeGenerators/LibUnicode/GeneratorUtil.h diff --git a/Meta/Lagom/Tools/CodeGenerators/LibUnicode/GenerateUnicodeData.cpp b/Meta/Lagom/Tools/CodeGenerators/LibUnicode/GenerateUnicodeData.cpp index 5a029c597e..01e4d8f261 100644 --- a/Meta/Lagom/Tools/CodeGenerators/LibUnicode/GenerateUnicodeData.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/LibUnicode/GenerateUnicodeData.cpp @@ -4,6 +4,7 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include "GeneratorUtil.h" #include #include #include @@ -560,7 +561,6 @@ static void generate_unicode_data_implementation(Core::File& file, UnicodeData c #include #include #include -#include #include #include #include @@ -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 const& aliases) { - generator.set("enum_title", enum_title); - generator.set("enum_snake", enum_snake); + HashValueMap 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 @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); diff --git a/Meta/Lagom/Tools/CodeGenerators/LibUnicode/GenerateUnicodeLocale.cpp b/Meta/Lagom/Tools/CodeGenerators/LibUnicode/GenerateUnicodeLocale.cpp index 3a119bcb48..65aed82c9e 100644 --- a/Meta/Lagom/Tools/CodeGenerators/LibUnicode/GenerateUnicodeLocale.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/LibUnicode/GenerateUnicodeLocale.cpp @@ -4,6 +4,7 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include "GeneratorUtil.h" #include #include #include @@ -666,7 +667,7 @@ static void generate_unicode_locale_implementation(Core::File& file, UnicodeLoca generator.append(R"~~~( #include -#include +#include #include #include #include @@ -1043,67 +1044,24 @@ Optional get_locale_@enum_snake@_mapping(StringView locale, StringVi )~~~"); }; - auto append_from_string = [&](StringView enum_title, StringView enum_snake, Vector 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 hashes; + hashes.ensure_capacity(values.size()); - generator.append(R"~~~( -Optional<@enum_title@> @enum_snake@_from_string(StringView const& @enum_snake@) -{ - static HashMap @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 hashes; + hashes.ensure_capacity(aliases.size()); - generator.append(R"~~~( -Optional resolve_@enum_snake@_alias(StringView const& @enum_snake@) -{ - static HashMap @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()); diff --git a/Meta/Lagom/Tools/CodeGenerators/LibUnicode/GeneratorUtil.h b/Meta/Lagom/Tools/CodeGenerators/LibUnicode/GeneratorUtil.h new file mode 100644 index 0000000000..5b6b126f2f --- /dev/null +++ b/Meta/Lagom/Tools/CodeGenerators/LibUnicode/GeneratorUtil.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2021, Tim Flynn + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +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 +struct HashValuePair { + unsigned hash { 0 }; + ValueType value {}; +}; + +template +struct HashValueComparator +{ + constexpr int operator()(unsigned hash, HashValuePair const& pair) + { + if (hash > pair.hash) + return 1; + if (hash < pair.hash) + return -1; + return 0; + } +}; +)~~~"); + + generated_from_string_types = true; +} + +template +using HashValueMap = HashMap; + +template +void generate_value_from_string(SourceGenerator& generator, StringView method_name_format, StringView value_type, StringView value_name, HashValueMap hashes, Optional 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, @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) + 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 {}; +} +)~~~"); +}