RESOLVED FIXED 224733
Refactor sorted array mapping machinery in LocaleToScriptMapping.cpp for reuse elsewhere
https://bugs.webkit.org/show_bug.cgi?id=224733
Summary Refactor sorted array mapping machinery in LocaleToScriptMapping.cpp for reus...
Darin Adler
Reported 2021-04-18 12:24:38 PDT
Refactor sorted array mapping machinery in LocaleToScriptMapping.cpp for reuse elsewhere
Attachments
Patch (17.25 KB, patch)
2021-04-18 12:30 PDT, Darin Adler
no flags
Patch (22.55 KB, patch)
2021-04-18 15:22 PDT, Darin Adler
ews-feeder: commit-queue-
Patch (21.18 KB, patch)
2021-04-18 15:31 PDT, Darin Adler
ews-feeder: commit-queue-
Patch (26.29 KB, patch)
2021-04-18 16:15 PDT, Darin Adler
ews-feeder: commit-queue-
Patch (26.30 KB, patch)
2021-04-18 16:25 PDT, Darin Adler
no flags
Patch (30.34 KB, patch)
2021-04-19 10:02 PDT, Darin Adler
ysuzuki: review+
Darin Adler
Comment 1 2021-04-18 12:30:27 PDT
Yusuke Suzuki
Comment 2 2021-04-18 12:45:48 PDT
Comment on attachment 426382 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=426382&action=review > Source/WebCore/platform/text/LocaleToScriptMapping.cpp:231 > + static const SortedArrayMap map { scriptNameCodeList }; Can we make it constexpr? This map becomes runtime constructed one so that this cannot be used in multithreaded case without call_once and LazyMeverDestroyed.
Darin Adler
Comment 3 2021-04-18 13:18:54 PDT
Comment on attachment 426382 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=426382&action=review >> Source/WebCore/platform/text/LocaleToScriptMapping.cpp:231 >> + static const SortedArrayMap map { scriptNameCodeList }; > > Can we make it constexpr? This map becomes runtime constructed one so that this cannot be used in multithreaded case without call_once and LazyMeverDestroyed. That might be difficult: 1) I don’t think I can use references or pointers in constexpr code, but maybe I can figure it out. 2) We want to run one-time code to check the sorting and uniqueness of keys in the map in debug builds when this is constructed. Not sure I can do that in a constexpr constructor. Your point about making sure it’s thread-safe is a good one. I am looking for a good solution. Would appreciate your advice if you have ideas.
Darin Adler
Comment 4 2021-04-18 13:46:22 PDT
Maybe I can make it constexpr with the array as a deduced template argument? If I could, then I could use call_once in the constructor to do the one-time sorted checking. But I don’t think template arguments can be arrays of such complex types.
Darin Adler
Comment 5 2021-04-18 14:09:21 PDT
(In reply to Darin Adler from comment #3) > 2) We want to run one-time code to check the sorting and uniqueness of keys > in the map in debug builds when this is constructed. Not sure I can do that > in a constexpr constructor. C++20 has constexpr std::is_sorted; we could check the sorting at compile time if we had that.
Darin Adler
Comment 6 2021-04-18 14:09:50 PDT
Hope that wouldn't make us compile too slowly, but it would be nice!
Darin Adler
Comment 7 2021-04-18 15:22:54 PDT
Darin Adler
Comment 8 2021-04-18 15:24:40 PDT
New version of the patch solves the thread safety problem by not using globals for SortedArrayMap objects, which are simple adapters. Still probably doesn't have includes right for non-unified builds. Also used this in one place in WebKit as a test for wider applicability. Backed off using KeyValuePair. Even though the member names are much nicer, std::pair handles generic programming much better, and it would be considerable work to fix KeyValuePair to remedy those problems. Yusuke, hoping for more feedback from you.
Darin Adler
Comment 9 2021-04-18 15:31:39 PDT
Darin Adler
Comment 10 2021-04-18 16:15:59 PDT
Darin Adler
Comment 11 2021-04-18 16:25:47 PDT
Yusuke Suzuki
Comment 12 2021-04-18 23:08:44 PDT
Comment on attachment 426382 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=426382&action=review >>> Source/WebCore/platform/text/LocaleToScriptMapping.cpp:231 >>> + static const SortedArrayMap map { scriptNameCodeList }; >> >> Can we make it constexpr? This map becomes runtime constructed one so that this cannot be used in multithreaded case without call_once and LazyMeverDestroyed. > > That might be difficult: > > 1) I don’t think I can use references or pointers in constexpr code, but maybe I can figure it out. > > 2) We want to run one-time code to check the sorting and uniqueness of keys in the map in debug builds when this is constructed. Not sure I can do that in a constexpr constructor. > > Your point about making sure it’s thread-safe is a good one. I am looking for a good solution. Would appreciate your advice if you have ideas. 1) We can use references in constexpr! 2) If we construct it in constexpr, then it is called only at compile-time, and one time per map since we are putting it in cpp file. I've tried making it constexpr by implementing adjacent_find and is_sorted for constexpr (since it becomes constexpr only after C++20 unfortunately. But I think adding an implementation for this debug purpose would be nice since the function is fairly small). And I've ensured that this can be a constexpr :) I think this is really nice: it becomes compile-error when the conditions are not met :D There are several notes. 1. After C++17, lambda can be a constexpr! So we can use lambda in constexpr freely :D 2. After C++17, std::begin and std::end are constexpr so we can use it too! 75 template<typename Iterator, typename Predicate> 76 constexpr Iterator adjacentFindConstexpr(Iterator begin, Iterator end, Predicate predicate) 77 { 78 if (begin == end) 79 return end; 80 auto current = begin; 81 auto previous = current; 82 ++current; 83 while (current != end) { 84 if (predicate(*previous, *current)) 85 return previous; 86 previous = current; 87 ++current; 88 } 89 return end; 90 } 91 92 template<typename Iterator, typename Predicate> 93 constexpr bool isSortedConstexpr(Iterator begin, Iterator end, Predicate predicate) 94 { 95 return adjacentFindConstexpr(begin, end, [&](auto& a, auto& b) { 96 return !predicate(a, b); 97 }) == end; 98 } 99 100 template<typename ArrayType> 101 constexpr SortedArrayMap<ArrayType>::SortedArrayMap(const ArrayType& array) 102 : m_array { array } 103 { 104 ASSERT_UNDER_CONSTEXPR_CONTEXT(isSortedConstexpr(std::begin(array), std::end(array), [] (auto& a, auto b) { 105 return a.first < b.first; 106 })); 107 ASSERT_UNDER_CONSTEXPR_CONTEXT(adjacentFindConstexpr(std::begin(array), std::end(array), [] (auto& a, auto& b) { 108 return a.first == b.first; 109 }) == std::end(array)); 110 } And using it via, 39 static constexpr std::pair<unsigned, unsigned> test[] = { 40 { 0, 0 }, 41 { 1, 1 }, 42 }; 43 44 static constexpr SortedArrayMap mapping { test }; This completely works! L44 does not specify array type in SortedArrayMap<XXX>'s XXX place since we can leverage C++17 template type deduction! This avoids writing array-size explicitly, so everything works well without specifying size etc...
Yusuke Suzuki
Comment 13 2021-04-18 23:11:28 PDT
Comment on attachment 426382 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=426382&action=review >>>> Source/WebCore/platform/text/LocaleToScriptMapping.cpp:231 >>>> + static const SortedArrayMap map { scriptNameCodeList }; >>> >>> Can we make it constexpr? This map becomes runtime constructed one so that this cannot be used in multithreaded case without call_once and LazyMeverDestroyed. >> >> That might be difficult: >> >> 1) I don’t think I can use references or pointers in constexpr code, but maybe I can figure it out. >> >> 2) We want to run one-time code to check the sorting and uniqueness of keys in the map in debug builds when this is constructed. Not sure I can do that in a constexpr constructor. >> >> Your point about making sure it’s thread-safe is a good one. I am looking for a good solution. Would appreciate your advice if you have ideas. > > 1) We can use references in constexpr! > 2) If we construct it in constexpr, then it is called only at compile-time, and one time per map since we are putting it in cpp file. > > I've tried making it constexpr by implementing adjacent_find and is_sorted for constexpr (since it becomes constexpr only after C++20 unfortunately. But I think adding an implementation for this debug purpose would be nice since the function is fairly small). > And I've ensured that this can be a constexpr :) > I think this is really nice: it becomes compile-error when the conditions are not met :D > > There are several notes. > > 1. After C++17, lambda can be a constexpr! So we can use lambda in constexpr freely :D > 2. After C++17, std::begin and std::end are constexpr so we can use it too! > > > 75 template<typename Iterator, typename Predicate> > 76 constexpr Iterator adjacentFindConstexpr(Iterator begin, Iterator end, Predicate predicate) > 77 { > 78 if (begin == end) > 79 return end; > 80 auto current = begin; > 81 auto previous = current; > 82 ++current; > 83 while (current != end) { > 84 if (predicate(*previous, *current)) > 85 return previous; > 86 previous = current; > 87 ++current; > 88 } > 89 return end; > 90 } > 91 > 92 template<typename Iterator, typename Predicate> > 93 constexpr bool isSortedConstexpr(Iterator begin, Iterator end, Predicate predicate) > 94 { > 95 return adjacentFindConstexpr(begin, end, [&](auto& a, auto& b) { > 96 return !predicate(a, b); > 97 }) == end; > 98 } > 99 > 100 template<typename ArrayType> > 101 constexpr SortedArrayMap<ArrayType>::SortedArrayMap(const ArrayType& array) > 102 : m_array { array } > 103 { > 104 ASSERT_UNDER_CONSTEXPR_CONTEXT(isSortedConstexpr(std::begin(array), std::end(array), [] (auto& a, auto b) { > 105 return a.first < b.first; > 106 })); > 107 ASSERT_UNDER_CONSTEXPR_CONTEXT(adjacentFindConstexpr(std::begin(array), std::end(array), [] (auto& a, auto& b) { > 108 return a.first == b.first; > 109 }) == std::end(array)); > 110 } > > And using it via, > > 39 static constexpr std::pair<unsigned, unsigned> test[] = { > 40 { 0, 0 }, > 41 { 1, 1 }, > 42 }; > 43 > 44 static constexpr SortedArrayMap mapping { test }; > > This completely works! L44 does not specify array type in SortedArrayMap<XXX>'s XXX place since we can leverage C++17 template type deduction! > This avoids writing array-size explicitly, so everything works well without specifying size etc... And if we use something like, 39 static constexpr std::pair<unsigned, unsigned> test[] = { 40 { 1, 0 }, 41 { 1, 1 }, 42 }; 43 44 static constexpr SortedArrayMap mapping { test }; We can get awesome compile-error since this doesn't meet sorted array's ASSERT_UNDER_CONSTEXPR_CONTEXT. /Volumes/Data/OpenSource/Source/WTF/wtf/SortedArrayMap.cpp:44:33: error: constexpr variable 'mapping' must be initialized by a constant expression static constexpr SortedArrayMap mapping { test }; ^~~~~~~~~~~~~~~~ In file included from /Volumes/Data/OpenSource/Source/WTF/wtf/SortedArrayMap.cpp:28: /Volumes/Data/OpenSource/Source/WTF/wtf/SortedArrayMap.h:104:5: note: non-constexpr function 'WTFReportAssertionFailure' cannot be used in a constant expression ASSERT_UNDER_CONSTEXPR_CONTEXT(isSortedConstexpr(std::begin(array), std::end(array), [] (auto& a, auto b) { ^ In file included from /Volumes/Data/OpenSource/Source/WTF/wtf/SortedArrayMap.cpp:27: In file included from /Volumes/Data/OpenSource/Source/WTF/config.h:31: In file included from /Volumes/Data/OpenSource/WebKitBuild/Debug/usr/local/include/wtf/FastMalloc.h:26: In file included from /Volumes/Data/OpenSource/WebKitBuild/Debug/usr/local/include/wtf/StdLibExtras.h:32: /Volumes/Data/OpenSource/WebKitBuild/Debug/usr/local/include/wtf/Assertions.h:354:9: note: expanded from macro 'ASSERT_UNDER_CONSTEXPR_CONTEXT' WTFReportAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #assertion); \ ^ /Volumes/Data/OpenSource/Source/WTF/wtf/SortedArrayMap.cpp:44:33: note: in call to 'SortedArrayMap(test)' static constexpr SortedArrayMap mapping { test }; ^ In file included from /Volumes/Data/OpenSource/Source/WTF/wtf/SortedArrayMap.cpp:27: In file included from /Volumes/Data/OpenSource/Source/WTF/config.h:31: In file included from /Volumes/Data/OpenSource/WebKitBuild/Debug/usr/local/include/wtf/FastMalloc.h:26: In file included from /Volumes/Data/OpenSource/WebKitBuild/Debug/usr/local/include/wtf/StdLibExtras.h:32: /Volumes/Data/OpenSource/WebKitBuild/Debug/usr/local/include/wtf/Assertions.h:204:25: note: declared here WTF_EXPORT_PRIVATE void WTFReportAssertionFailure(const char* file, int line, const char* function, const char* assertion); ^ 1 error generated. ** BUILD FAILED **
Darin Adler
Comment 14 2021-04-19 09:24:54 PDT
Thanks for the encouragement, and for the constexpr implementations of is_sorted and adjacent_find.
Darin Adler
Comment 15 2021-04-19 10:02:38 PDT
Darin Adler
Comment 16 2021-04-19 14:46:14 PDT
Yusuke, took all your suggestions and now this seems ready to go! Would you be willing to review?
Yusuke Suzuki
Comment 17 2021-04-19 14:56:27 PDT
Comment on attachment 426438 [details] Patch r=me!
Darin Adler
Comment 18 2021-04-20 07:48:14 PDT
Ling Ho
Comment 19 2021-04-23 02:50:08 PDT
Note You need to log in before you can comment on or make changes to this bug.