diff --git a/Siv3D/include/ThirdParty/absl/algorithm/container.h b/Siv3D/include/ThirdParty/absl/algorithm/container.h index f0cee94f5d..679e02676c 100644 --- a/Siv3D/include/ThirdParty/absl/algorithm/container.h +++ b/Siv3D/include/ThirdParty/absl/algorithm/container.h @@ -77,9 +77,8 @@ using ContainerIterPairType = decltype(std::make_pair(ContainerIter(), ContainerIter())); template -using ContainerDifferenceType = - decltype(std::distance(std::declval>(), - std::declval>())); +using ContainerDifferenceType = decltype(std::distance( + std::declval>(), std::declval>())); template using ContainerPointerType = @@ -90,17 +89,21 @@ using ContainerPointerType = // lookup of std::begin and std::end, i.e. // using std::begin; // using std::end; -// std::foo(begin(c), end(c); +// std::foo(begin(c), end(c)); // becomes // std::foo(container_algorithm_internal::begin(c), -// container_algorithm_internal::end(c)); +// container_algorithm_internal::end(c)); // These are meant for internal use only. template -ContainerIter c_begin(C& c) { return begin(c); } +ContainerIter c_begin(C& c) { + return begin(c); +} template -ContainerIter c_end(C& c) { return end(c); } +ContainerIter c_end(C& c) { + return end(c); +} template struct IsUnorderedContainer : std::false_type {}; @@ -166,7 +169,7 @@ container_algorithm_internal::ContainerDifferenceType c_distance( // c_all_of() // // Container-based version of the `std::all_of()` function to -// test a condition on all elements within a container. +// test if all elements within a container satisfy a condition. template bool c_all_of(const C& c, Pred&& pred) { return std::all_of(container_algorithm_internal::c_begin(c), @@ -188,7 +191,7 @@ bool c_any_of(const C& c, Pred&& pred) { // c_none_of() // // Container-based version of the `std::none_of()` function to -// test if no elements in a container fulfil a condition. +// test if no elements in a container fulfill a condition. template bool c_none_of(const C& c, Pred&& pred) { return std::none_of(container_algorithm_internal::c_begin(c), @@ -343,8 +346,8 @@ container_algorithm_internal::ContainerDifferenceType c_count_if( // return the first element where two ordered containers differ. Applies `==` to // the first N elements of `c1` and `c2`, where N = min(size(c1), size(c2)). template -container_algorithm_internal::ContainerIterPairType -c_mismatch(C1& c1, C2& c2) { +container_algorithm_internal::ContainerIterPairType c_mismatch(C1& c1, + C2& c2) { auto first1 = container_algorithm_internal::c_begin(c1); auto last1 = container_algorithm_internal::c_end(c1); auto first2 = container_algorithm_internal::c_begin(c2); @@ -365,8 +368,8 @@ c_mismatch(C1& c1, C2& c2) { // the function's test condition. Applies `pred`to the first N elements of `c1` // and `c2`, where N = min(size(c1), size(c2)). template -container_algorithm_internal::ContainerIterPairType -c_mismatch(C1& c1, C2& c2, BinaryPredicate pred) { +container_algorithm_internal::ContainerIterPairType c_mismatch( + C1& c1, C2& c2, BinaryPredicate pred) { auto first1 = container_algorithm_internal::c_begin(c1); auto last1 = container_algorithm_internal::c_end(c1); auto first2 = container_algorithm_internal::c_begin(c2); @@ -655,11 +658,10 @@ OutputIterator c_replace_copy(const C& c, OutputIterator result, T&& old_value, // some condition, and return the results within an iterator. template OutputIterator c_replace_copy_if(const C& c, OutputIterator result, Pred&& pred, - T&& new_value) { + const T& new_value) { return std::replace_copy_if(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), result, - std::forward(pred), - std::forward(new_value)); + std::forward(pred), new_value); } // c_fill() @@ -667,9 +669,9 @@ OutputIterator c_replace_copy_if(const C& c, OutputIterator result, Pred&& pred, // Container-based version of the `std::fill()` function to fill a // container with some value. template -void c_fill(C& c, T&& value) { +void c_fill(C& c, const T& value) { std::fill(container_algorithm_internal::c_begin(c), - container_algorithm_internal::c_end(c), std::forward(value)); + container_algorithm_internal::c_end(c), value); } // c_fill_n() @@ -677,9 +679,8 @@ void c_fill(C& c, T&& value) { // Container-based version of the `std::fill_n()` function to fill // the first N elements in a container with some value. template -void c_fill_n(C& c, Size n, T&& value) { - std::fill_n(container_algorithm_internal::c_begin(c), n, - std::forward(value)); +void c_fill_n(C& c, Size n, const T& value) { + std::fill_n(container_algorithm_internal::c_begin(c), n, value); } // c_generate() @@ -716,10 +717,11 @@ container_algorithm_internal::ContainerIter c_generate_n(C& c, Size n, // copy a container's elements while removing any elements matching the given // `value`. template -OutputIterator c_remove_copy(const C& c, OutputIterator result, T&& value) { +OutputIterator c_remove_copy(const C& c, OutputIterator result, + const T& value) { return std::remove_copy(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), result, - std::forward(value)); + value); } // c_remove_copy_if() @@ -905,11 +907,11 @@ void c_sort(C& c) { // Overload of c_sort() for performing a `comp` comparison other than the // default `operator<`. -template -void c_sort(C& c, Compare&& comp) { +template +void c_sort(C& c, LessThan&& comp) { std::sort(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), - std::forward(comp)); + std::forward(comp)); } // c_stable_sort() @@ -925,11 +927,11 @@ void c_stable_sort(C& c) { // Overload of c_stable_sort() for performing a `comp` comparison other than the // default `operator<`. -template -void c_stable_sort(C& c, Compare&& comp) { +template +void c_stable_sort(C& c, LessThan&& comp) { std::stable_sort(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), - std::forward(comp)); + std::forward(comp)); } // c_is_sorted() @@ -944,11 +946,11 @@ bool c_is_sorted(const C& c) { // c_is_sorted() overload for performing a `comp` comparison other than the // default `operator<`. -template -bool c_is_sorted(const C& c, Compare&& comp) { +template +bool c_is_sorted(const C& c, LessThan&& comp) { return std::is_sorted(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), - std::forward(comp)); + std::forward(comp)); } // c_partial_sort() @@ -966,14 +968,14 @@ void c_partial_sort( // Overload of c_partial_sort() for performing a `comp` comparison other than // the default `operator<`. -template +template void c_partial_sort( RandomAccessContainer& sequence, container_algorithm_internal::ContainerIter middle, - Compare&& comp) { + LessThan&& comp) { std::partial_sort(container_algorithm_internal::c_begin(sequence), middle, container_algorithm_internal::c_end(sequence), - std::forward(comp)); + std::forward(comp)); } // c_partial_sort_copy() @@ -994,15 +996,15 @@ c_partial_sort_copy(const C& sequence, RandomAccessContainer& result) { // Overload of c_partial_sort_copy() for performing a `comp` comparison other // than the default `operator<`. -template +template container_algorithm_internal::ContainerIter c_partial_sort_copy(const C& sequence, RandomAccessContainer& result, - Compare&& comp) { + LessThan&& comp) { return std::partial_sort_copy(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), container_algorithm_internal::c_begin(result), container_algorithm_internal::c_end(result), - std::forward(comp)); + std::forward(comp)); } // c_is_sorted_until() @@ -1018,12 +1020,12 @@ container_algorithm_internal::ContainerIter c_is_sorted_until(C& c) { // Overload of c_is_sorted_until() for performing a `comp` comparison other than // the default `operator<`. -template +template container_algorithm_internal::ContainerIter c_is_sorted_until( - C& c, Compare&& comp) { + C& c, LessThan&& comp) { return std::is_sorted_until(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), - std::forward(comp)); + std::forward(comp)); } // c_nth_element() @@ -1043,14 +1045,14 @@ void c_nth_element( // Overload of c_nth_element() for performing a `comp` comparison other than // the default `operator<`. -template +template void c_nth_element( RandomAccessContainer& sequence, container_algorithm_internal::ContainerIter nth, - Compare&& comp) { + LessThan&& comp) { std::nth_element(container_algorithm_internal::c_begin(sequence), nth, container_algorithm_internal::c_end(sequence), - std::forward(comp)); + std::forward(comp)); } //------------------------------------------------------------------------------ @@ -1064,20 +1066,19 @@ void c_nth_element( // which does not compare less than `value`. template container_algorithm_internal::ContainerIter c_lower_bound( - Sequence& sequence, T&& value) { + Sequence& sequence, const T& value) { return std::lower_bound(container_algorithm_internal::c_begin(sequence), - container_algorithm_internal::c_end(sequence), - std::forward(value)); + container_algorithm_internal::c_end(sequence), value); } // Overload of c_lower_bound() for performing a `comp` comparison other than // the default `operator<`. -template +template container_algorithm_internal::ContainerIter c_lower_bound( - Sequence& sequence, T&& value, Compare&& comp) { + Sequence& sequence, const T& value, LessThan&& comp) { return std::lower_bound(container_algorithm_internal::c_begin(sequence), - container_algorithm_internal::c_end(sequence), - std::forward(value), std::forward(comp)); + container_algorithm_internal::c_end(sequence), value, + std::forward(comp)); } // c_upper_bound() @@ -1087,20 +1088,19 @@ container_algorithm_internal::ContainerIter c_lower_bound( // which is greater than `value`. template container_algorithm_internal::ContainerIter c_upper_bound( - Sequence& sequence, T&& value) { + Sequence& sequence, const T& value) { return std::upper_bound(container_algorithm_internal::c_begin(sequence), - container_algorithm_internal::c_end(sequence), - std::forward(value)); + container_algorithm_internal::c_end(sequence), value); } // Overload of c_upper_bound() for performing a `comp` comparison other than // the default `operator<`. -template +template container_algorithm_internal::ContainerIter c_upper_bound( - Sequence& sequence, T&& value, Compare&& comp) { + Sequence& sequence, const T& value, LessThan&& comp) { return std::upper_bound(container_algorithm_internal::c_begin(sequence), - container_algorithm_internal::c_end(sequence), - std::forward(value), std::forward(comp)); + container_algorithm_internal::c_end(sequence), value, + std::forward(comp)); } // c_equal_range() @@ -1110,20 +1110,19 @@ container_algorithm_internal::ContainerIter c_upper_bound( // sorted container which compare equal to `value`. template container_algorithm_internal::ContainerIterPairType -c_equal_range(Sequence& sequence, T&& value) { +c_equal_range(Sequence& sequence, const T& value) { return std::equal_range(container_algorithm_internal::c_begin(sequence), - container_algorithm_internal::c_end(sequence), - std::forward(value)); + container_algorithm_internal::c_end(sequence), value); } // Overload of c_equal_range() for performing a `comp` comparison other than // the default `operator<`. -template +template container_algorithm_internal::ContainerIterPairType -c_equal_range(Sequence& sequence, T&& value, Compare&& comp) { +c_equal_range(Sequence& sequence, const T& value, LessThan&& comp) { return std::equal_range(container_algorithm_internal::c_begin(sequence), - container_algorithm_internal::c_end(sequence), - std::forward(value), std::forward(comp)); + container_algorithm_internal::c_end(sequence), value, + std::forward(comp)); } // c_binary_search() @@ -1132,20 +1131,20 @@ c_equal_range(Sequence& sequence, T&& value, Compare&& comp) { // to test if any element in the sorted container contains a value equivalent to // 'value'. template -bool c_binary_search(Sequence&& sequence, T&& value) { +bool c_binary_search(const Sequence& sequence, const T& value) { return std::binary_search(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), - std::forward(value)); + value); } // Overload of c_binary_search() for performing a `comp` comparison other than // the default `operator<`. -template -bool c_binary_search(Sequence&& sequence, T&& value, Compare&& comp) { +template +bool c_binary_search(const Sequence& sequence, const T& value, + LessThan&& comp) { return std::binary_search(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), - std::forward(value), - std::forward(comp)); + value, std::forward(comp)); } //------------------------------------------------------------------------------ @@ -1166,14 +1165,14 @@ OutputIterator c_merge(const C1& c1, const C2& c2, OutputIterator result) { // Overload of c_merge() for performing a `comp` comparison other than // the default `operator<`. -template +template OutputIterator c_merge(const C1& c1, const C2& c2, OutputIterator result, - Compare&& comp) { + LessThan&& comp) { return std::merge(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), container_algorithm_internal::c_end(c2), result, - std::forward(comp)); + std::forward(comp)); } // c_inplace_merge() @@ -1189,13 +1188,13 @@ void c_inplace_merge(C& c, // Overload of c_inplace_merge() for performing a merge using a `comp` other // than `operator<`. -template +template void c_inplace_merge(C& c, container_algorithm_internal::ContainerIter middle, - Compare&& comp) { + LessThan&& comp) { std::inplace_merge(container_algorithm_internal::c_begin(c), middle, container_algorithm_internal::c_end(c), - std::forward(comp)); + std::forward(comp)); } // c_includes() @@ -1213,13 +1212,13 @@ bool c_includes(const C1& c1, const C2& c2) { // Overload of c_includes() for performing a merge using a `comp` other than // `operator<`. -template -bool c_includes(const C1& c1, const C2& c2, Compare&& comp) { +template +bool c_includes(const C1& c1, const C2& c2, LessThan&& comp) { return std::includes(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), container_algorithm_internal::c_end(c2), - std::forward(comp)); + std::forward(comp)); } // c_set_union() @@ -1243,7 +1242,7 @@ OutputIterator c_set_union(const C1& c1, const C2& c2, OutputIterator output) { // Overload of c_set_union() for performing a merge using a `comp` other than // `operator<`. -template ::value, void>::type, @@ -1251,18 +1250,18 @@ template ::value, void>::type> OutputIterator c_set_union(const C1& c1, const C2& c2, OutputIterator output, - Compare&& comp) { + LessThan&& comp) { return std::set_union(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), container_algorithm_internal::c_end(c2), output, - std::forward(comp)); + std::forward(comp)); } // c_set_intersection() // // Container-based version of the `std::set_intersection()` function -// to return an iterator containing the intersection of two containers. +// to return an iterator containing the intersection of two sorted containers. template ::value, @@ -1272,6 +1271,11 @@ template ::type> OutputIterator c_set_intersection(const C1& c1, const C2& c2, OutputIterator output) { + // In debug builds, ensure that both containers are sorted with respect to the + // default comparator. std::set_intersection requires the containers be sorted + // using operator<. + assert(absl::c_is_sorted(c1)); + assert(absl::c_is_sorted(c2)); return std::set_intersection(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), @@ -1280,7 +1284,7 @@ OutputIterator c_set_intersection(const C1& c1, const C2& c2, // Overload of c_set_intersection() for performing a merge using a `comp` other // than `operator<`. -template ::value, void>::type, @@ -1288,12 +1292,17 @@ template ::value, void>::type> OutputIterator c_set_intersection(const C1& c1, const C2& c2, - OutputIterator output, Compare&& comp) { + OutputIterator output, LessThan&& comp) { + // In debug builds, ensure that both containers are sorted with respect to the + // default comparator. std::set_intersection requires the containers be sorted + // using the same comparator. + assert(absl::c_is_sorted(c1, comp)); + assert(absl::c_is_sorted(c2, comp)); return std::set_intersection(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), container_algorithm_internal::c_end(c2), output, - std::forward(comp)); + std::forward(comp)); } // c_set_difference() @@ -1318,7 +1327,7 @@ OutputIterator c_set_difference(const C1& c1, const C2& c2, // Overload of c_set_difference() for performing a merge using a `comp` other // than `operator<`. -template ::value, void>::type, @@ -1326,12 +1335,12 @@ template ::value, void>::type> OutputIterator c_set_difference(const C1& c1, const C2& c2, - OutputIterator output, Compare&& comp) { + OutputIterator output, LessThan&& comp) { return std::set_difference(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), container_algorithm_internal::c_end(c2), output, - std::forward(comp)); + std::forward(comp)); } // c_set_symmetric_difference() @@ -1357,7 +1366,7 @@ OutputIterator c_set_symmetric_difference(const C1& c1, const C2& c2, // Overload of c_set_symmetric_difference() for performing a merge using a // `comp` other than `operator<`. -template ::value, void>::type, @@ -1366,13 +1375,13 @@ template ::type> OutputIterator c_set_symmetric_difference(const C1& c1, const C2& c2, OutputIterator output, - Compare&& comp) { + LessThan&& comp) { return std::set_symmetric_difference( container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), container_algorithm_internal::c_end(c2), output, - std::forward(comp)); + std::forward(comp)); } //------------------------------------------------------------------------------ @@ -1391,11 +1400,11 @@ void c_push_heap(RandomAccessContainer& sequence) { // Overload of c_push_heap() for performing a push operation on a heap using a // `comp` other than `operator<`. -template -void c_push_heap(RandomAccessContainer& sequence, Compare&& comp) { +template +void c_push_heap(RandomAccessContainer& sequence, LessThan&& comp) { std::push_heap(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), - std::forward(comp)); + std::forward(comp)); } // c_pop_heap() @@ -1410,11 +1419,11 @@ void c_pop_heap(RandomAccessContainer& sequence) { // Overload of c_pop_heap() for performing a pop operation on a heap using a // `comp` other than `operator<`. -template -void c_pop_heap(RandomAccessContainer& sequence, Compare&& comp) { +template +void c_pop_heap(RandomAccessContainer& sequence, LessThan&& comp) { std::pop_heap(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), - std::forward(comp)); + std::forward(comp)); } // c_make_heap() @@ -1429,11 +1438,11 @@ void c_make_heap(RandomAccessContainer& sequence) { // Overload of c_make_heap() for performing heap comparisons using a // `comp` other than `operator<` -template -void c_make_heap(RandomAccessContainer& sequence, Compare&& comp) { +template +void c_make_heap(RandomAccessContainer& sequence, LessThan&& comp) { std::make_heap(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), - std::forward(comp)); + std::forward(comp)); } // c_sort_heap() @@ -1448,11 +1457,11 @@ void c_sort_heap(RandomAccessContainer& sequence) { // Overload of c_sort_heap() for performing heap comparisons using a // `comp` other than `operator<` -template -void c_sort_heap(RandomAccessContainer& sequence, Compare&& comp) { +template +void c_sort_heap(RandomAccessContainer& sequence, LessThan&& comp) { std::sort_heap(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), - std::forward(comp)); + std::forward(comp)); } // c_is_heap() @@ -1467,11 +1476,11 @@ bool c_is_heap(const RandomAccessContainer& sequence) { // Overload of c_is_heap() for performing heap comparisons using a // `comp` other than `operator<` -template -bool c_is_heap(const RandomAccessContainer& sequence, Compare&& comp) { +template +bool c_is_heap(const RandomAccessContainer& sequence, LessThan&& comp) { return std::is_heap(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), - std::forward(comp)); + std::forward(comp)); } // c_is_heap_until() @@ -1487,12 +1496,12 @@ c_is_heap_until(RandomAccessContainer& sequence) { // Overload of c_is_heap_until() for performing heap comparisons using a // `comp` other than `operator<` -template +template container_algorithm_internal::ContainerIter -c_is_heap_until(RandomAccessContainer& sequence, Compare&& comp) { +c_is_heap_until(RandomAccessContainer& sequence, LessThan&& comp) { return std::is_heap_until(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), - std::forward(comp)); + std::forward(comp)); } //------------------------------------------------------------------------------ @@ -1513,12 +1522,12 @@ container_algorithm_internal::ContainerIter c_min_element( // Overload of c_min_element() for performing a `comp` comparison other than // `operator<`. -template +template container_algorithm_internal::ContainerIter c_min_element( - Sequence& sequence, Compare&& comp) { + Sequence& sequence, LessThan&& comp) { return std::min_element(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), - std::forward(comp)); + std::forward(comp)); } // c_max_element() @@ -1535,12 +1544,12 @@ container_algorithm_internal::ContainerIter c_max_element( // Overload of c_max_element() for performing a `comp` comparison other than // `operator<`. -template +template container_algorithm_internal::ContainerIter c_max_element( - Sequence& sequence, Compare&& comp) { + Sequence& sequence, LessThan&& comp) { return std::max_element(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), - std::forward(comp)); + std::forward(comp)); } // c_minmax_element() @@ -1550,20 +1559,20 @@ container_algorithm_internal::ContainerIter c_max_element( // smallest and largest values, respectively, using `operator<` to make the // comparisons. template -container_algorithm_internal::ContainerIterPairType -c_minmax_element(C& c) { +container_algorithm_internal::ContainerIterPairType c_minmax_element( + C& c) { return std::minmax_element(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c)); } // Overload of c_minmax_element() for performing `comp` comparisons other than // `operator<`. -template -container_algorithm_internal::ContainerIterPairType -c_minmax_element(C& c, Compare&& comp) { +template +container_algorithm_internal::ContainerIterPairType c_minmax_element( + C& c, LessThan&& comp) { return std::minmax_element(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), - std::forward(comp)); + std::forward(comp)); } //------------------------------------------------------------------------------ @@ -1578,7 +1587,8 @@ c_minmax_element(C& c, Compare&& comp) { // that capital letters ("A-Z") have ASCII values less than lowercase letters // ("a-z"). template -bool c_lexicographical_compare(Sequence1&& sequence1, Sequence2&& sequence2) { +bool c_lexicographical_compare(const Sequence1& sequence1, + const Sequence2& sequence2) { return std::lexicographical_compare( container_algorithm_internal::c_begin(sequence1), container_algorithm_internal::c_end(sequence1), @@ -1588,15 +1598,15 @@ bool c_lexicographical_compare(Sequence1&& sequence1, Sequence2&& sequence2) { // Overload of c_lexicographical_compare() for performing a lexicographical // comparison using a `comp` operator instead of `operator<`. -template -bool c_lexicographical_compare(Sequence1&& sequence1, Sequence2&& sequence2, - Compare&& comp) { +template +bool c_lexicographical_compare(const Sequence1& sequence1, + const Sequence2& sequence2, LessThan&& comp) { return std::lexicographical_compare( container_algorithm_internal::c_begin(sequence1), container_algorithm_internal::c_end(sequence1), container_algorithm_internal::c_begin(sequence2), container_algorithm_internal::c_end(sequence2), - std::forward(comp)); + std::forward(comp)); } // c_next_permutation() @@ -1612,11 +1622,11 @@ bool c_next_permutation(C& c) { // Overload of c_next_permutation() for performing a lexicographical // comparison using a `comp` operator instead of `operator<`. -template -bool c_next_permutation(C& c, Compare&& comp) { +template +bool c_next_permutation(C& c, LessThan&& comp) { return std::next_permutation(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), - std::forward(comp)); + std::forward(comp)); } // c_prev_permutation() @@ -1632,11 +1642,11 @@ bool c_prev_permutation(C& c) { // Overload of c_prev_permutation() for performing a lexicographical // comparison using a `comp` operator instead of `operator<`. -template -bool c_prev_permutation(C& c, Compare&& comp) { +template +bool c_prev_permutation(C& c, LessThan&& comp) { return std::prev_permutation(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), - std::forward(comp)); + std::forward(comp)); } //------------------------------------------------------------------------------ @@ -1645,18 +1655,18 @@ bool c_prev_permutation(C& c, Compare&& comp) { // c_iota() // -// Container-based version of the `std::iota()` function +// Container-based version of the `std::iota()` function // to compute successive values of `value`, as if incremented with `++value` // after each element is written. and write them to the container. template -void c_iota(Sequence& sequence, T&& value) { +void c_iota(Sequence& sequence, const T& value) { std::iota(container_algorithm_internal::c_begin(sequence), - container_algorithm_internal::c_end(sequence), - std::forward(value)); + container_algorithm_internal::c_end(sequence), value); } + // c_accumulate() // -// Container-based version of the `std::accumulate()` function +// Container-based version of the `std::accumulate()` function // to accumulate the element values of a container to `init` and return that // accumulation by value. // @@ -1683,7 +1693,7 @@ decay_t c_accumulate(const Sequence& sequence, T&& init, // c_inner_product() // -// Container-based version of the `std::inner_product()` function +// Container-based version of the `std::inner_product()` function // to compute the cumulative inner product of container element pairs. // // Note: Due to a language technicality this function has return type @@ -1714,7 +1724,7 @@ decay_t c_inner_product(const Sequence1& factors1, const Sequence2& factors2, // c_adjacent_difference() // -// Container-based version of the `std::adjacent_difference()` +// Container-based version of the `std::adjacent_difference()` // function to compute the difference between each element and the one preceding // it and write it to an iterator. template @@ -1737,7 +1747,7 @@ OutputIt c_adjacent_difference(const InputSequence& input, // c_partial_sum() // -// Container-based version of the `std::partial_sum()` function +// Container-based version of the `std::partial_sum()` function // to compute the partial sum of the elements in a sequence and write them // to an iterator. The partial sum is the sum of all element values so far in // the sequence. diff --git a/Siv3D/include/ThirdParty/absl/base/attributes.h b/Siv3D/include/ThirdParty/absl/base/attributes.h index 046fbea350..a7f279a025 100644 --- a/Siv3D/include/ThirdParty/absl/base/attributes.h +++ b/Siv3D/include/ThirdParty/absl/base/attributes.h @@ -18,8 +18,6 @@ // These macros are used within Abseil and allow the compiler to optimize, where // applicable, certain function calls. // -// This file is used for both C and C++! -// // Most macros here are exposing GCC or Clang features, and are stubbed out for // other compilers. // @@ -121,7 +119,7 @@ #if ABSL_HAVE_ATTRIBUTE(disable_tail_calls) #define ABSL_HAVE_ATTRIBUTE_NO_TAIL_CALL 1 #define ABSL_ATTRIBUTE_NO_TAIL_CALL __attribute__((disable_tail_calls)) -#elif defined(__GNUC__) && !defined(__clang__) +#elif defined(__GNUC__) && !defined(__clang__) && !defined(__e2k__) #define ABSL_HAVE_ATTRIBUTE_NO_TAIL_CALL 1 #define ABSL_ATTRIBUTE_NO_TAIL_CALL \ __attribute__((optimize("no-optimize-sibling-calls"))) @@ -133,14 +131,15 @@ // ABSL_ATTRIBUTE_WEAK // // Tags a function as weak for the purposes of compilation and linking. -// Weak attributes currently do not work properly in LLVM's Windows backend, -// so disable them there. See https://bugs.llvm.org/show_bug.cgi?id=37598 +// Weak attributes did not work properly in LLVM's Windows backend before +// 9.0.0, so disable them there. See https://bugs.llvm.org/show_bug.cgi?id=37598 // for further information. // The MinGW compiler doesn't complain about the weak attribute until the link // step, presumably because Windows doesn't use ELF binaries. -#if (ABSL_HAVE_ATTRIBUTE(weak) || \ - (defined(__GNUC__) && !defined(__clang__))) && \ - !(defined(__llvm__) && defined(_WIN32)) && !defined(__MINGW32__) +#if (ABSL_HAVE_ATTRIBUTE(weak) || \ + (defined(__GNUC__) && !defined(__clang__))) && \ + (!defined(_WIN32) || (defined(__clang__) && __clang_major__ >= 9)) && \ + !defined(__MINGW32__) #undef ABSL_ATTRIBUTE_WEAK #define ABSL_ATTRIBUTE_WEAK __attribute__((weak)) #define ABSL_HAVE_ATTRIBUTE_WEAK 1 @@ -212,8 +211,20 @@ // out of bounds or does other scary things with memory. // NOTE: GCC supports AddressSanitizer(asan) since 4.8. // https://gcc.gnu.org/gcc-4.8/changes.html -#if ABSL_HAVE_ATTRIBUTE(no_sanitize_address) +#if defined(ABSL_HAVE_ADDRESS_SANITIZER) && \ + ABSL_HAVE_ATTRIBUTE(no_sanitize_address) #define ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address)) +#elif defined(ABSL_HAVE_ADDRESS_SANITIZER) && defined(_MSC_VER) && \ + _MSC_VER >= 1928 +// https://docs.microsoft.com/en-us/cpp/cpp/no-sanitize-address +#define ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS __declspec(no_sanitize_address) +#elif defined(ABSL_HAVE_HWADDRESS_SANITIZER) && ABSL_HAVE_ATTRIBUTE(no_sanitize) +// HWAddressSanitizer is a sanitizer similar to AddressSanitizer, which uses CPU +// features to detect similar bugs with less CPU and memory overhead. +// NOTE: GCC supports HWAddressSanitizer(hwasan) since 11. +// https://gcc.gnu.org/gcc-11/changes.html +#define ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS \ + __attribute__((no_sanitize("hwaddress"))) #else #define ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS #endif @@ -263,7 +274,7 @@ // // Tells the ControlFlowIntegrity sanitizer to not instrument a given function. // See https://clang.llvm.org/docs/ControlFlowIntegrity.html for details. -#if ABSL_HAVE_ATTRIBUTE(no_sanitize) +#if ABSL_HAVE_ATTRIBUTE(no_sanitize) && defined(__llvm__) #define ABSL_ATTRIBUTE_NO_SANITIZE_CFI __attribute__((no_sanitize("cfi"))) #else #define ABSL_ATTRIBUTE_NO_SANITIZE_CFI @@ -283,10 +294,7 @@ // ABSL_ATTRIBUTE_RETURNS_NONNULL // // Tells the compiler that a particular function never returns a null pointer. -#if ABSL_HAVE_ATTRIBUTE(returns_nonnull) || \ - (defined(__GNUC__) && \ - (__GNUC__ > 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)) && \ - !defined(__clang__)) +#if ABSL_HAVE_ATTRIBUTE(returns_nonnull) #define ABSL_ATTRIBUTE_RETURNS_NONNULL __attribute__((returns_nonnull)) #else #define ABSL_ATTRIBUTE_RETURNS_NONNULL @@ -316,15 +324,22 @@ __attribute__((section(#name))) __attribute__((noinline)) #endif - // ABSL_ATTRIBUTE_SECTION_VARIABLE // // Tells the compiler/linker to put a given variable into a section and define // `__start_ ## name` and `__stop_ ## name` symbols to bracket the section. // This functionality is supported by GNU linker. #ifndef ABSL_ATTRIBUTE_SECTION_VARIABLE +#ifdef _AIX +// __attribute__((section(#name))) on AIX is achieved by using the `.csect` +// psudo op which includes an additional integer as part of its syntax indcating +// alignment. If data fall under different alignments then you might get a +// compilation error indicating a `Section type conflict`. +#define ABSL_ATTRIBUTE_SECTION_VARIABLE(name) +#else #define ABSL_ATTRIBUTE_SECTION_VARIABLE(name) __attribute__((section(#name))) #endif +#endif // ABSL_DECLARE_ATTRIBUTE_SECTION_VARS // @@ -335,8 +350,8 @@ // a no-op on ELF but not on Mach-O. // #ifndef ABSL_DECLARE_ATTRIBUTE_SECTION_VARS -#define ABSL_DECLARE_ATTRIBUTE_SECTION_VARS(name) \ - extern char __start_##name[] ABSL_ATTRIBUTE_WEAK; \ +#define ABSL_DECLARE_ATTRIBUTE_SECTION_VARS(name) \ + extern char __start_##name[] ABSL_ATTRIBUTE_WEAK; \ extern char __stop_##name[] ABSL_ATTRIBUTE_WEAK #endif #ifndef ABSL_DEFINE_ATTRIBUTE_SECTION_VARS @@ -397,6 +412,9 @@ // // Tells the compiler to warn about unused results. // +// For code or headers that are assured to only build with C++17 and up, prefer +// just using the standard `[[nodiscard]]` directly over this macro. +// // When annotating a function, it must appear as the first part of the // declaration or definition. The compiler will warn if the return value from // such a function is unused: @@ -423,9 +441,10 @@ // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66425 // // Note: past advice was to place the macro after the argument list. -#if ABSL_HAVE_ATTRIBUTE(nodiscard) -#define ABSL_MUST_USE_RESULT [[nodiscard]] -#elif defined(__clang__) && ABSL_HAVE_ATTRIBUTE(warn_unused_result) +// +// TODO(b/176172494): Use ABSL_HAVE_CPP_ATTRIBUTE(nodiscard) when all code is +// compliant with the stricter [[nodiscard]]. +#if defined(__clang__) && ABSL_HAVE_ATTRIBUTE(warn_unused_result) #define ABSL_MUST_USE_RESULT __attribute__((warn_unused_result)) #else #define ABSL_MUST_USE_RESULT @@ -495,7 +514,7 @@ #define ABSL_XRAY_NEVER_INSTRUMENT [[clang::xray_never_instrument]] #if ABSL_HAVE_CPP_ATTRIBUTE(clang::xray_log_args) #define ABSL_XRAY_LOG_ARGS(N) \ - [[clang::xray_always_instrument, clang::xray_log_args(N)]] + [[clang::xray_always_instrument, clang::xray_log_args(N)]] #else #define ABSL_XRAY_LOG_ARGS(N) [[clang::xray_always_instrument]] #endif @@ -526,6 +545,13 @@ // ABSL_ATTRIBUTE_UNUSED // // Prevents the compiler from complaining about variables that appear unused. +// +// For code or headers that are assured to only build with C++17 and up, prefer +// just using the standard '[[maybe_unused]]' directly over this macro. +// +// Due to differences in positioning requirements between the old, compiler +// specific __attribute__ syntax and the now standard [[maybe_unused]], this +// macro does not attempt to take advantage of '[[maybe_unused]]'. #if ABSL_HAVE_ATTRIBUTE(unused) || (defined(__GNUC__) && !defined(__clang__)) #undef ABSL_ATTRIBUTE_UNUSED #define ABSL_ATTRIBUTE_UNUSED __attribute__((__unused__)) @@ -546,13 +572,19 @@ // ABSL_ATTRIBUTE_PACKED // // Instructs the compiler not to use natural alignment for a tagged data -// structure, but instead to reduce its alignment to 1. This attribute can -// either be applied to members of a structure or to a structure in its -// entirety. Applying this attribute (judiciously) to a structure in its -// entirety to optimize the memory footprint of very commonly-used structs is -// fine. Do not apply this attribute to a structure in its entirety if the -// purpose is to control the offsets of the members in the structure. Instead, -// apply this attribute only to structure members that need it. +// structure, but instead to reduce its alignment to 1. +// +// Therefore, DO NOT APPLY THIS ATTRIBUTE TO STRUCTS CONTAINING ATOMICS. Doing +// so can cause atomic variables to be mis-aligned and silently violate +// atomicity on x86. +// +// This attribute can either be applied to members of a structure or to a +// structure in its entirety. Applying this attribute (judiciously) to a +// structure in its entirety to optimize the memory footprint of very +// commonly-used structs is fine. Do not apply this attribute to a structure in +// its entirety if the purpose is to control the offsets of the members in the +// structure. Instead, apply this attribute only to structure members that need +// it. // // When applying ABSL_ATTRIBUTE_PACKED only to specific structure members the // natural alignment of structure members not annotated is preserved. Aligned @@ -597,30 +629,24 @@ // case 42: // ... // -// Notes: when compiled with clang in C++11 mode, the ABSL_FALLTHROUGH_INTENDED -// macro is expanded to the [[clang::fallthrough]] attribute, which is analysed -// when performing switch labels fall-through diagnostic -// (`-Wimplicit-fallthrough`). See clang documentation on language extensions -// for details: +// Notes: When supported, GCC and Clang can issue a warning on switch labels +// with unannotated fallthrough using the warning `-Wimplicit-fallthrough`. See +// clang documentation on language extensions for details: // https://clang.llvm.org/docs/AttributeReference.html#fallthrough-clang-fallthrough // -// When used with unsupported compilers, the ABSL_FALLTHROUGH_INTENDED macro -// has no effect on diagnostics. In any case this macro has no effect on runtime +// When used with unsupported compilers, the ABSL_FALLTHROUGH_INTENDED macro has +// no effect on diagnostics. In any case this macro has no effect on runtime // behavior and performance of code. + #ifdef ABSL_FALLTHROUGH_INTENDED #error "ABSL_FALLTHROUGH_INTENDED should not be defined." -#endif - -// TODO(zhangxy): Use c++17 standard [[fallthrough]] macro, when supported. -#if defined(__clang__) && defined(__has_warning) -#if __has_feature(cxx_attributes) && __has_warning("-Wimplicit-fallthrough") +#elif ABSL_HAVE_CPP_ATTRIBUTE(fallthrough) +#define ABSL_FALLTHROUGH_INTENDED [[fallthrough]] +#elif ABSL_HAVE_CPP_ATTRIBUTE(clang::fallthrough) #define ABSL_FALLTHROUGH_INTENDED [[clang::fallthrough]] -#endif -#elif defined(__GNUC__) && __GNUC__ >= 7 +#elif ABSL_HAVE_CPP_ATTRIBUTE(gnu::fallthrough) #define ABSL_FALLTHROUGH_INTENDED [[gnu::fallthrough]] -#endif - -#ifndef ABSL_FALLTHROUGH_INTENDED +#else #define ABSL_FALLTHROUGH_INTENDED \ do { \ } while (0) @@ -632,6 +658,9 @@ // declarations. The macro argument is used as a custom diagnostic message (e.g. // suggestion of a better alternative). // +// For code or headers that are assured to only build with C++14 and up, prefer +// just using the standard `[[deprecated("message")]]` directly over this macro. +// // Examples: // // class ABSL_DEPRECATED("Use Bar instead") Foo {...}; @@ -642,26 +671,60 @@ // ABSL_DEPRECATED("Use DoThat() instead") // void DoThis(); // +// enum FooEnum { +// kBar ABSL_DEPRECATED("Use kBaz instead"), +// }; +// // Every usage of a deprecated entity will trigger a warning when compiled with -// clang's `-Wdeprecated-declarations` option. This option is turned off by -// default, but the warnings will be reported by clang-tidy. -#if defined(__clang__) && __cplusplus >= 201103L +// GCC/Clang's `-Wdeprecated-declarations` option. Google's production toolchain +// turns this warning off by default, instead relying on clang-tidy to report +// new uses of deprecated code. +#if ABSL_HAVE_ATTRIBUTE(deprecated) #define ABSL_DEPRECATED(message) __attribute__((deprecated(message))) -#endif - -#ifndef ABSL_DEPRECATED +#else #define ABSL_DEPRECATED(message) #endif +// When deprecating Abseil code, it is sometimes necessary to turn off the +// warning within Abseil, until the deprecated code is actually removed. The +// deprecated code can be surrounded with these directives to acheive that +// result. +// +// class ABSL_DEPRECATED("Use Bar instead") Foo; +// +// ABSL_INTERNAL_DISABLE_DEPRECATED_DECLARATION_WARNING +// Baz ComputeBazFromFoo(Foo f); +// ABSL_INTERNAL_RESTORE_DEPRECATED_DECLARATION_WARNING +#if defined(__GNUC__) || defined(__clang__) +// Clang also supports these GCC pragmas. +#define ABSL_INTERNAL_DISABLE_DEPRECATED_DECLARATION_WARNING \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#define ABSL_INTERNAL_RESTORE_DEPRECATED_DECLARATION_WARNING \ + _Pragma("GCC diagnostic pop") +#else +#define ABSL_INTERNAL_DISABLE_DEPRECATED_DECLARATION_WARNING +#define ABSL_INTERNAL_RESTORE_DEPRECATED_DECLARATION_WARNING +#endif // defined(__GNUC__) || defined(__clang__) + // ABSL_CONST_INIT // // A variable declaration annotated with the `ABSL_CONST_INIT` attribute will // not compile (on supported platforms) unless the variable has a constant // initializer. This is useful for variables with static and thread storage // duration, because it guarantees that they will not suffer from the so-called -// "static init order fiasco". Prefer to put this attribute on the most visible -// declaration of the variable, if there's more than one, because code that -// accesses the variable can then use the attribute for optimization. +// "static init order fiasco". +// +// This attribute must be placed on the initializing declaration of the +// variable. Some compilers will give a -Wmissing-constinit warning when this +// attribute is placed on some other declaration but missing from the +// initializing declaration. +// +// In some cases (notably with thread_local variables), `ABSL_CONST_INIT` can +// also be used in a non-initializing declaration to tell the compiler that a +// variable is already initialized, reducing overhead that would otherwise be +// incurred by a hidden guard variable. Thus annotating all declarations with +// this attribute is recommended to potentially enhance optimization. // // Example: // @@ -670,13 +733,103 @@ // ABSL_CONST_INIT static MyType my_var; // }; // -// MyType MyClass::my_var = MakeMyType(...); +// ABSL_CONST_INIT MyType MyClass::my_var = MakeMyType(...); +// +// For code or headers that are assured to only build with C++20 and up, prefer +// just using the standard `constinit` keyword directly over this macro. // // Note that this attribute is redundant if the variable is declared constexpr. -#if ABSL_HAVE_CPP_ATTRIBUTE(clang::require_constant_initialization) +#if defined(__cpp_constinit) && __cpp_constinit >= 201907L +#define ABSL_CONST_INIT constinit +#elif ABSL_HAVE_CPP_ATTRIBUTE(clang::require_constant_initialization) #define ABSL_CONST_INIT [[clang::require_constant_initialization]] #else #define ABSL_CONST_INIT -#endif // ABSL_HAVE_CPP_ATTRIBUTE(clang::require_constant_initialization) +#endif + +// These annotations are not available yet due to fear of breaking code. +#define ABSL_ATTRIBUTE_PURE_FUNCTION +#define ABSL_ATTRIBUTE_CONST_FUNCTION + +// ABSL_ATTRIBUTE_LIFETIME_BOUND indicates that a resource owned by a function +// parameter or implicit object parameter is retained by the return value of the +// annotated function (or, for a parameter of a constructor, in the value of the +// constructed object). This attribute causes warnings to be produced if a +// temporary object does not live long enough. +// +// When applied to a reference parameter, the referenced object is assumed to be +// retained by the return value of the function. When applied to a non-reference +// parameter (for example, a pointer or a class type), all temporaries +// referenced by the parameter are assumed to be retained by the return value of +// the function. +// +// See also the upstream documentation: +// https://clang.llvm.org/docs/AttributeReference.html#lifetimebound +#if ABSL_HAVE_CPP_ATTRIBUTE(clang::lifetimebound) +#define ABSL_ATTRIBUTE_LIFETIME_BOUND [[clang::lifetimebound]] +#elif ABSL_HAVE_ATTRIBUTE(lifetimebound) +#define ABSL_ATTRIBUTE_LIFETIME_BOUND __attribute__((lifetimebound)) +#else +#define ABSL_ATTRIBUTE_LIFETIME_BOUND +#endif + +// ABSL_ATTRIBUTE_TRIVIAL_ABI +// Indicates that a type is "trivially relocatable" -- meaning it can be +// relocated without invoking the constructor/destructor, using a form of move +// elision. +// +// From a memory safety point of view, putting aside destructor ordering, it's +// safe to apply ABSL_ATTRIBUTE_TRIVIAL_ABI if an object's location +// can change over the course of its lifetime: if a constructor can be run one +// place, and then the object magically teleports to another place where some +// methods are run, and then the object teleports to yet another place where it +// is destroyed. This is notably not true for self-referential types, where the +// move-constructor must keep the self-reference up to date. If the type changed +// location without invoking the move constructor, it would have a dangling +// self-reference. +// +// The use of this teleporting machinery means that the number of paired +// move/destroy operations can change, and so it is a bad idea to apply this to +// a type meant to count the number of moves. +// +// Warning: applying this can, rarely, break callers. Objects passed by value +// will be destroyed at the end of the call, instead of the end of the +// full-expression containing the call. In addition, it changes the ABI +// of functions accepting this type by value (e.g. to pass in registers). +// +// See also the upstream documentation: +// https://clang.llvm.org/docs/AttributeReference.html#trivial-abi +// +#if ABSL_HAVE_CPP_ATTRIBUTE(clang::trivial_abi) +#define ABSL_ATTRIBUTE_TRIVIAL_ABI [[clang::trivial_abi]] +#define ABSL_HAVE_ATTRIBUTE_TRIVIAL_ABI 1 +#elif ABSL_HAVE_ATTRIBUTE(trivial_abi) +#define ABSL_ATTRIBUTE_TRIVIAL_ABI __attribute__((trivial_abi)) +#define ABSL_HAVE_ATTRIBUTE_TRIVIAL_ABI 1 +#else +#define ABSL_ATTRIBUTE_TRIVIAL_ABI +#endif + +// ABSL_ATTRIBUTE_NO_UNIQUE_ADDRESS +// +// Indicates a data member can be optimized to occupy no space (if it is empty) +// and/or its tail padding can be used for other members. +// +// For code that is assured to only build with C++20 or later, prefer using +// the standard attribute `[[no_unique_address]]` directly instead of this +// macro. +// +// https://devblogs.microsoft.com/cppblog/msvc-cpp20-and-the-std-cpp20-switch/#c20-no_unique_address +// Current versions of MSVC have disabled `[[no_unique_address]]` since it +// breaks ABI compatibility, but offers `[[msvc::no_unique_address]]` for +// situations when it can be assured that it is desired. Since Abseil does not +// claim ABI compatibility in mixed builds, we can offer it unconditionally. +#if defined(_MSC_VER) && _MSC_VER >= 1929 +#define ABSL_ATTRIBUTE_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]] +#elif ABSL_HAVE_CPP_ATTRIBUTE(no_unique_address) +#define ABSL_ATTRIBUTE_NO_UNIQUE_ADDRESS [[no_unique_address]] +#else +#define ABSL_ATTRIBUTE_NO_UNIQUE_ADDRESS +#endif #endif // ABSL_BASE_ATTRIBUTES_H_ diff --git a/Siv3D/include/ThirdParty/absl/base/call_once.h b/Siv3D/include/ThirdParty/absl/base/call_once.h index 5b468af855..08436bac8a 100644 --- a/Siv3D/include/ThirdParty/absl/base/call_once.h +++ b/Siv3D/include/ThirdParty/absl/base/call_once.h @@ -123,7 +123,7 @@ class SchedulingHelper { private: base_internal::SchedulingMode mode_; - bool guard_result_; + bool guard_result_ = false; }; // Bit patterns for call_once state machine values. Internal implementation @@ -177,15 +177,8 @@ void CallOnceImpl(std::atomic* control, scheduling_mode) == kOnceInit) { base_internal::invoke(std::forward(fn), std::forward(args)...); - // The call to SpinLockWake below is an optimization, because the waiter - // in SpinLockWait is waiting with a short timeout. The atomic load/store - // sequence is slightly faster than an atomic exchange: - // old_control = control->exchange(base_internal::kOnceDone, - // std::memory_order_release); - // We opt for a slightly faster case when there are no waiters, in spite - // of longer tail latency when there are waiters. - old_control = control->load(std::memory_order_relaxed); - control->store(base_internal::kOnceDone, std::memory_order_release); + old_control = + control->exchange(base_internal::kOnceDone, std::memory_order_release); if (old_control == base_internal::kOnceWaiter) { base_internal::SpinLockWake(control, true); } diff --git a/Siv3D/include/ThirdParty/absl/base/casts.h b/Siv3D/include/ThirdParty/absl/base/casts.h index 83c691265f..d19588852f 100644 --- a/Siv3D/include/ThirdParty/absl/base/casts.h +++ b/Siv3D/include/ThirdParty/absl/base/casts.h @@ -29,6 +29,10 @@ #include #include +#if defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L +#include // For std::bit_cast. +#endif // defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L + #include "absl/base/internal/identity.h" #include "absl/base/macros.h" #include "absl/meta/type_traits.h" @@ -36,19 +40,6 @@ namespace absl { ABSL_NAMESPACE_BEGIN -namespace internal_casts { - -template -struct is_bitcastable - : std::integral_constant< - bool, - sizeof(Dest) == sizeof(Source) && - type_traits_internal::is_trivially_copyable::value && - type_traits_internal::is_trivially_copyable::value && - std::is_default_constructible::value> {}; - -} // namespace internal_casts - // implicit_cast() // // Performs an implicit conversion between types following the language @@ -105,81 +96,83 @@ constexpr To implicit_cast(typename absl::internal::identity_t to) { // bit_cast() // -// Performs a bitwise cast on a type without changing the underlying bit -// representation of that type's value. The two types must be of the same size -// and both types must be trivially copyable. As with most casts, use with -// caution. A `bit_cast()` might be needed when you need to temporarily treat a -// type as some other type, such as in the following cases: -// -// * Serialization (casting temporarily to `char *` for those purposes is -// always allowed by the C++ standard) -// * Managing the individual bits of a type within mathematical operations -// that are not normally accessible through that type -// * Casting non-pointer types to pointer types (casting the other way is -// allowed by `reinterpret_cast()` but round-trips cannot occur the other -// way). +// Creates a value of the new type `Dest` whose representation is the same as +// that of the argument, which is of (deduced) type `Source` (a "bitwise cast"; +// every bit in the value representation of the result is equal to the +// corresponding bit in the object representation of the source). Source and +// destination types must be of the same size, and both types must be trivially +// copyable. // -// Example: +// As with most casts, use with caution. A `bit_cast()` might be needed when you +// need to treat a value as the value of some other type, for example, to access +// the individual bits of an object which are not normally accessible through +// the object's type, such as for working with the binary representation of a +// floating point value: // // float f = 3.14159265358979; -// int i = bit_cast(f); +// int i = bit_cast(f); // // i = 0x40490fdb // -// Casting non-pointer types to pointer types and then dereferencing them -// traditionally produces undefined behavior. +// Reinterpreting and accessing a value directly as a different type (as shown +// below) usually results in undefined behavior. // // Example: // // // WRONG -// float f = 3.14159265358979; // WRONG -// int i = * reinterpret_cast(&f); // WRONG +// float f = 3.14159265358979; +// int i = reinterpret_cast(f); // Wrong +// int j = *reinterpret_cast(&f); // Equally wrong +// int k = *bit_cast(&f); // Equally wrong +// +// Reinterpret-casting results in undefined behavior according to the ISO C++ +// specification, section [basic.lval]. Roughly, this section says: if an object +// in memory has one type, and a program accesses it with a different type, the +// result is undefined behavior for most "different type". // -// The address-casting method produces undefined behavior according to the ISO -// C++ specification section [basic.lval]. Roughly, this section says: if an -// object in memory has one type, and a program accesses it with a different -// type, the result is undefined behavior for most values of "different type". +// Using bit_cast on a pointer and then dereferencing it is no better than using +// reinterpret_cast. You should only use bit_cast on the value itself. // // Such casting results in type punning: holding an object in memory of one type // and reading its bits back using a different type. A `bit_cast()` avoids this -// issue by implementing its casts using `memcpy()`, which avoids introducing -// this undefined behavior. -// -// NOTE: The requirements here are more strict than the bit_cast of standard -// proposal p0476 due to the need for workarounds and lack of intrinsics. -// Specifically, this implementation also requires `Dest` to be -// default-constructible. +// issue by copying the object representation to a new value, which avoids +// introducing this undefined behavior (since the original value is never +// accessed in the wrong way). +// +// The requirements of `absl::bit_cast` are more strict than that of +// `std::bit_cast` unless compiler support is available. Specifically, without +// compiler support, this implementation also requires `Dest` to be +// default-constructible. In C++20, `absl::bit_cast` is replaced by +// `std::bit_cast`. +#if defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L + +using std::bit_cast; + +#else // defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L + template < typename Dest, typename Source, - typename std::enable_if::value, + typename std::enable_if::value && + std::is_trivially_copyable::value +#if !ABSL_HAVE_BUILTIN(__builtin_bit_cast) + && std::is_default_constructible::value +#endif // !ABSL_HAVE_BUILTIN(__builtin_bit_cast) + , int>::type = 0> +#if ABSL_HAVE_BUILTIN(__builtin_bit_cast) +inline constexpr Dest bit_cast(const Source& source) { + return __builtin_bit_cast(Dest, source); +} +#else // ABSL_HAVE_BUILTIN(__builtin_bit_cast) inline Dest bit_cast(const Source& source) { Dest dest; memcpy(static_cast(std::addressof(dest)), static_cast(std::addressof(source)), sizeof(dest)); return dest; } +#endif // ABSL_HAVE_BUILTIN(__builtin_bit_cast) -// NOTE: This overload is only picked if the requirements of bit_cast are -// not met. It is therefore UB, but is provided temporarily as previous -// versions of this function template were unchecked. Do not use this in -// new code. -template < - typename Dest, typename Source, - typename std::enable_if< - !internal_casts::is_bitcastable::value, - int>::type = 0> -ABSL_DEPRECATED( - "absl::bit_cast type requirements were violated. Update the types " - "being used such that they are the same size and are both " - "TriviallyCopyable.") -inline Dest bit_cast(const Source& source) { - static_assert(sizeof(Dest) == sizeof(Source), - "Source and destination types should have equal sizes."); - - Dest dest; - memcpy(&dest, &source, sizeof(dest)); - return dest; -} +#endif // defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L ABSL_NAMESPACE_END } // namespace absl diff --git a/Siv3D/include/ThirdParty/absl/base/config.h b/Siv3D/include/ThirdParty/absl/base/config.h index c1d0494eed..ff0c2978c2 100644 --- a/Siv3D/include/ThirdParty/absl/base/config.h +++ b/Siv3D/include/ThirdParty/absl/base/config.h @@ -56,6 +56,25 @@ #include #endif // __cplusplus +// ABSL_INTERNAL_CPLUSPLUS_LANG +// +// MSVC does not set the value of __cplusplus correctly, but instead uses +// _MSVC_LANG as a stand-in. +// https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros +// +// However, there are reports that MSVC even sets _MSVC_LANG incorrectly at +// times, for example: +// https://github.com/microsoft/vscode-cpptools/issues/1770 +// https://reviews.llvm.org/D70996 +// +// For this reason, this symbol is considered INTERNAL and code outside of +// Abseil must not use it. +#if defined(_MSVC_LANG) +#define ABSL_INTERNAL_CPLUSPLUS_LANG _MSVC_LANG +#elif defined(__cplusplus) +#define ABSL_INTERNAL_CPLUSPLUS_LANG __cplusplus +#endif + #if defined(__APPLE__) // Included for TARGET_OS_IPHONE, __IPHONE_OS_VERSION_MIN_REQUIRED, // __IPHONE_8_0. @@ -66,6 +85,35 @@ #include "absl/base/options.h" #include "absl/base/policy_checks.h" +// Abseil long-term support (LTS) releases will define +// `ABSL_LTS_RELEASE_VERSION` to the integer representing the date string of the +// LTS release version, and will define `ABSL_LTS_RELEASE_PATCH_LEVEL` to the +// integer representing the patch-level for that release. +// +// For example, for LTS release version "20300401.2", this would give us +// ABSL_LTS_RELEASE_VERSION == 20300401 && ABSL_LTS_RELEASE_PATCH_LEVEL == 2 +// +// These symbols will not be defined in non-LTS code. +// +// Abseil recommends that clients live-at-head. Therefore, if you are using +// these symbols to assert a minimum version requirement, we recommend you do it +// as +// +// #if defined(ABSL_LTS_RELEASE_VERSION) && ABSL_LTS_RELEASE_VERSION < 20300401 +// #error Project foo requires Abseil LTS version >= 20300401 +// #endif +// +// The `defined(ABSL_LTS_RELEASE_VERSION)` part of the check excludes +// live-at-head clients from the minimum version assertion. +// +// See https://abseil.io/about/releases for more information on Abseil release +// management. +// +// LTS releases can be obtained from +// https://github.com/abseil/abseil-cpp/releases. +#define ABSL_LTS_RELEASE_VERSION 20230802 +#define ABSL_LTS_RELEASE_PATCH_LEVEL 1 + // Helper macro to convert a CPP variable to a string literal. #define ABSL_INTERNAL_DO_TOKEN_STR(x) #x #define ABSL_INTERNAL_TOKEN_STR(x) ABSL_INTERNAL_DO_TOKEN_STR(x) @@ -121,10 +169,16 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #if ABSL_OPTION_USE_INLINE_NAMESPACE == 0 #define ABSL_NAMESPACE_BEGIN #define ABSL_NAMESPACE_END +#define ABSL_INTERNAL_C_SYMBOL(x) x #elif ABSL_OPTION_USE_INLINE_NAMESPACE == 1 #define ABSL_NAMESPACE_BEGIN \ inline namespace ABSL_OPTION_INLINE_NAMESPACE_NAME { #define ABSL_NAMESPACE_END } +#define ABSL_INTERNAL_C_SYMBOL_HELPER_2(x, v) x##_##v +#define ABSL_INTERNAL_C_SYMBOL_HELPER_1(x, v) \ + ABSL_INTERNAL_C_SYMBOL_HELPER_2(x, v) +#define ABSL_INTERNAL_C_SYMBOL(x) \ + ABSL_INTERNAL_C_SYMBOL_HELPER_1(x, ABSL_OPTION_INLINE_NAMESPACE_NAME) #else #error options.h is misconfigured. #endif @@ -148,39 +202,43 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #define ABSL_HAVE_BUILTIN(x) 0 #endif -#if defined(__is_identifier) -#define ABSL_INTERNAL_HAS_KEYWORD(x) !(__is_identifier(x)) -#else -#define ABSL_INTERNAL_HAS_KEYWORD(x) 0 -#endif - #ifdef __has_feature #define ABSL_HAVE_FEATURE(f) __has_feature(f) #else #define ABSL_HAVE_FEATURE(f) 0 #endif +// Portable check for GCC minimum version: +// https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html +#if defined(__GNUC__) && defined(__GNUC_MINOR__) +#define ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(x, y) \ + (__GNUC__ > (x) || __GNUC__ == (x) && __GNUC_MINOR__ >= (y)) +#else +#define ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(x, y) 0 +#endif + +#if defined(__clang__) && defined(__clang_major__) && defined(__clang_minor__) +#define ABSL_INTERNAL_HAVE_MIN_CLANG_VERSION(x, y) \ + (__clang_major__ > (x) || __clang_major__ == (x) && __clang_minor__ >= (y)) +#else +#define ABSL_INTERNAL_HAVE_MIN_CLANG_VERSION(x, y) 0 +#endif + // ABSL_HAVE_TLS is defined to 1 when __thread should be supported. -// We assume __thread is supported on Linux when compiled with Clang or compiled -// against libstdc++ with _GLIBCXX_HAVE_TLS defined. +// We assume __thread is supported on Linux or Asylo when compiled with Clang or +// compiled against libstdc++ with _GLIBCXX_HAVE_TLS defined. #ifdef ABSL_HAVE_TLS #error ABSL_HAVE_TLS cannot be directly set -#elif defined(__linux__) && (defined(__clang__) || defined(_GLIBCXX_HAVE_TLS)) +#elif (defined(__linux__) || defined(__ASYLO__)) && \ + (defined(__clang__) || defined(_GLIBCXX_HAVE_TLS)) #define ABSL_HAVE_TLS 1 #endif // ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE // // Checks whether `std::is_trivially_destructible` is supported. -// -// Notes: All supported compilers using libc++ support this feature, as does -// gcc >= 4.8.1 using libstdc++, and Visual Studio. #ifdef ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE #error ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE cannot be directly set -#elif defined(_LIBCPP_VERSION) || \ - (!defined(__clang__) && defined(__GNUC__) && defined(__GLIBCXX__) && \ - (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) || \ - defined(_MSC_VER) #define ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE 1 #endif @@ -188,35 +246,27 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || // // Checks whether `std::is_trivially_default_constructible` and // `std::is_trivially_copy_constructible` are supported. +#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE +#error ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE cannot be directly set +#else +#define ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE 1 +#endif // ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE // // Checks whether `std::is_trivially_copy_assignable` is supported. - -// Notes: Clang with libc++ supports these features, as does gcc >= 5.1 with -// either libc++ or libstdc++, and Visual Studio (but not NVCC). -#if defined(ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE) -#error ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE cannot be directly set -#elif defined(ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE) -#error ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE cannot directly set -#elif (defined(__clang__) && defined(_LIBCPP_VERSION)) || \ - (!defined(__clang__) && defined(__GNUC__) && \ - (__GNUC__ > 7 || (__GNUC__ == 7 && __GNUC_MINOR__ >= 4)) && \ - (defined(_LIBCPP_VERSION) || defined(__GLIBCXX__))) || \ - (defined(_MSC_VER) && !defined(__NVCC__)) -#define ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE 1 +#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE +#error ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE cannot be directly set +#else #define ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE 1 #endif -// ABSL_HAVE_SOURCE_LOCATION_CURRENT +// ABSL_HAVE_STD_IS_TRIVIALLY_COPYABLE // -// Indicates whether `absl::SourceLocation::current()` will return useful -// information in some contexts. -#ifndef ABSL_HAVE_SOURCE_LOCATION_CURRENT -#if ABSL_INTERNAL_HAS_KEYWORD(__builtin_LINE) && \ - ABSL_INTERNAL_HAS_KEYWORD(__builtin_FILE) -#define ABSL_HAVE_SOURCE_LOCATION_CURRENT 1 -#endif +// Checks whether `std::is_trivially_copyable` is supported. +#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_COPYABLE +#error ABSL_HAVE_STD_IS_TRIVIALLY_COPYABLE cannot be directly set +#define ABSL_HAVE_STD_IS_TRIVIALLY_COPYABLE 1 #endif // ABSL_HAVE_THREAD_LOCAL @@ -311,25 +361,21 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || // For further details, consult the compiler's documentation. #ifdef ABSL_HAVE_EXCEPTIONS #error ABSL_HAVE_EXCEPTIONS cannot be directly set. - -#elif defined(__clang__) - -#if __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 6) +#elif ABSL_INTERNAL_HAVE_MIN_CLANG_VERSION(3, 6) // Clang >= 3.6 #if ABSL_HAVE_FEATURE(cxx_exceptions) #define ABSL_HAVE_EXCEPTIONS 1 #endif // ABSL_HAVE_FEATURE(cxx_exceptions) -#else +#elif defined(__clang__) // Clang < 3.6 // http://releases.llvm.org/3.6.0/tools/clang/docs/ReleaseNotes.html#the-exceptions-macro #if defined(__EXCEPTIONS) && ABSL_HAVE_FEATURE(cxx_exceptions) #define ABSL_HAVE_EXCEPTIONS 1 #endif // defined(__EXCEPTIONS) && ABSL_HAVE_FEATURE(cxx_exceptions) -#endif // __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 6) - // Handle remaining special cases and default to exceptions being supported. -#elif !(defined(__GNUC__) && (__GNUC__ < 5) && !defined(__EXCEPTIONS)) && \ - !(defined(__GNUC__) && (__GNUC__ >= 5) && !defined(__cpp_exceptions)) && \ +#elif !(defined(__GNUC__) && (__GNUC__ < 5) && !defined(__EXCEPTIONS)) && \ + !(ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(5, 0) && \ + !defined(__cpp_exceptions)) && \ !(defined(_MSC_VER) && !defined(_CPPUNWIND)) #define ABSL_HAVE_EXCEPTIONS 1 #endif @@ -361,10 +407,12 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || // POSIX.1-2001. #ifdef ABSL_HAVE_MMAP #error ABSL_HAVE_MMAP cannot be directly set -#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ - defined(__ros__) || defined(__native_client__) || defined(__asmjs__) || \ - defined(__wasm__) || defined(__Fuchsia__) || defined(__sun) || \ - defined(__ASYLO__) +#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ + defined(_AIX) || defined(__ros__) || defined(__native_client__) || \ + defined(__asmjs__) || defined(__wasm__) || defined(__Fuchsia__) || \ + defined(__sun) || defined(__ASYLO__) || defined(__myriad2__) || \ + defined(__HAIKU__) || defined(__OpenBSD__) || defined(__NetBSD__) || \ + defined(__QNX__) || defined(__VXWORKS__) || defined(__hexagon__) #define ABSL_HAVE_MMAP 1 #endif @@ -375,17 +423,28 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #ifdef ABSL_HAVE_PTHREAD_GETSCHEDPARAM #error ABSL_HAVE_PTHREAD_GETSCHEDPARAM cannot be directly set #elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ - defined(__ros__) + defined(_AIX) || defined(__ros__) || defined(__OpenBSD__) || \ + defined(__NetBSD__) || defined(__VXWORKS__) #define ABSL_HAVE_PTHREAD_GETSCHEDPARAM 1 #endif +// ABSL_HAVE_SCHED_GETCPU +// +// Checks whether sched_getcpu is available. +#ifdef ABSL_HAVE_SCHED_GETCPU +#error ABSL_HAVE_SCHED_GETCPU cannot be directly set +#elif defined(__linux__) +#define ABSL_HAVE_SCHED_GETCPU 1 +#endif + // ABSL_HAVE_SCHED_YIELD // // Checks whether the platform implements sched_yield(2) as defined in // POSIX.1-2001. #ifdef ABSL_HAVE_SCHED_YIELD #error ABSL_HAVE_SCHED_YIELD cannot be directly set -#elif defined(__linux__) || defined(__ros__) || defined(__native_client__) +#elif defined(__linux__) || defined(__ros__) || defined(__native_client__) || \ + defined(__VXWORKS__) #define ABSL_HAVE_SCHED_YIELD 1 #endif @@ -400,7 +459,7 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || // platforms. #ifdef ABSL_HAVE_SEMAPHORE_H #error ABSL_HAVE_SEMAPHORE_H cannot be directly set -#elif defined(__linux__) || defined(__ros__) +#elif defined(__linux__) || defined(__ros__) || defined(__VXWORKS__) #define ABSL_HAVE_SEMAPHORE_H 1 #endif @@ -428,6 +487,8 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #elif defined(__Fuchsia__) // Signals don't exist on fuchsia. #elif defined(__native_client__) +// Signals don't exist on hexagon/QuRT +#elif defined(__hexagon__) #else // other standard libraries #define ABSL_HAVE_ALARM 1 @@ -461,22 +522,29 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #error "absl endian detection needs to be set up for your compiler" #endif -// macOS 10.13 and iOS 10.11 don't let you use , , or -// even though the headers exist and are publicly noted to work. See +// macOS < 10.13 and iOS < 12 don't support , , or +// because the libc++ shared library shipped on the system doesn't have the +// requisite exported symbols. See // https://github.com/abseil/abseil-cpp/issues/207 and // https://developer.apple.com/documentation/xcode_release_notes/xcode_10_release_notes +// // libc++ spells out the availability requirements in the file // llvm-project/libcxx/include/__config via the #define -// _LIBCPP_AVAILABILITY_BAD_OPTIONAL_ACCESS. -#if defined(__APPLE__) && defined(_LIBCPP_VERSION) && \ - ((defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \ - __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101400) || \ - (defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) && \ - __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ < 120000) || \ - (defined(__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__) && \ - __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ < 50000) || \ - (defined(__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__) && \ - __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ < 120000)) +// _LIBCPP_AVAILABILITY_BAD_OPTIONAL_ACCESS. The set of versions has been +// modified a few times, via +// https://github.com/llvm/llvm-project/commit/7fb40e1569dd66292b647f4501b85517e9247953 +// and +// https://github.com/llvm/llvm-project/commit/0bc451e7e137c4ccadcd3377250874f641ca514a +// The second has the actually correct versions, thus, is what we copy here. +#if defined(__APPLE__) && \ + ((defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \ + __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101300) || \ + (defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) && \ + __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ < 120000) || \ + (defined(__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__) && \ + __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ < 50000) || \ + (defined(__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__) && \ + __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ < 120000)) #define ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE 1 #else #define ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE 0 @@ -484,71 +552,44 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || // ABSL_HAVE_STD_ANY // -// Checks whether C++17 std::any is available by checking whether exists. +// Checks whether C++17 std::any is available. #ifdef ABSL_HAVE_STD_ANY #error "ABSL_HAVE_STD_ANY cannot be directly set." -#endif - -#ifdef __has_include -#if __has_include() && __cplusplus >= 201703L && \ +#elif defined(ABSL_INTERNAL_CPLUSPLUS_LANG) && \ + ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L && \ !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE #define ABSL_HAVE_STD_ANY 1 #endif -#endif // ABSL_HAVE_STD_OPTIONAL // // Checks whether C++17 std::optional is available. #ifdef ABSL_HAVE_STD_OPTIONAL #error "ABSL_HAVE_STD_OPTIONAL cannot be directly set." -#endif - -#ifdef __has_include -#if __has_include() && __cplusplus >= 201703L && \ +#elif defined(ABSL_INTERNAL_CPLUSPLUS_LANG) && \ + ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L && \ !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE #define ABSL_HAVE_STD_OPTIONAL 1 #endif -#endif // ABSL_HAVE_STD_VARIANT // // Checks whether C++17 std::variant is available. #ifdef ABSL_HAVE_STD_VARIANT #error "ABSL_HAVE_STD_VARIANT cannot be directly set." -#endif - -#ifdef __has_include -#if __has_include() && __cplusplus >= 201703L && \ +#elif defined(ABSL_INTERNAL_CPLUSPLUS_LANG) && \ + ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L && \ !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE #define ABSL_HAVE_STD_VARIANT 1 #endif -#endif // ABSL_HAVE_STD_STRING_VIEW // // Checks whether C++17 std::string_view is available. #ifdef ABSL_HAVE_STD_STRING_VIEW #error "ABSL_HAVE_STD_STRING_VIEW cannot be directly set." -#endif - -#ifdef __has_include -#if __has_include() && __cplusplus >= 201703L -#define ABSL_HAVE_STD_STRING_VIEW 1 -#endif -#endif - -// For MSVC, `__has_include` is supported in VS 2017 15.3, which is later than -// the support for , , , . So we use -// _MSC_VER to check whether we have VS 2017 RTM (when , , -// , is implemented) or higher. Also, `__cplusplus` is -// not correctly set by MSVC, so we use `_MSVC_LANG` to check the language -// version. -// TODO(zhangxy): fix tests before enabling aliasing for `std::any`. -#if defined(_MSC_VER) && _MSC_VER >= 1910 && \ - ((defined(_MSVC_LANG) && _MSVC_LANG > 201402) || __cplusplus > 201402) -// #define ABSL_HAVE_STD_ANY 1 -#define ABSL_HAVE_STD_OPTIONAL 1 -#define ABSL_HAVE_STD_VARIANT 1 +#elif defined(ABSL_INTERNAL_CPLUSPLUS_LANG) && \ + ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L #define ABSL_HAVE_STD_STRING_VIEW 1 #endif @@ -644,8 +685,6 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #endif #endif -#undef ABSL_INTERNAL_HAS_KEYWORD - // ABSL_DLL // // When building Abseil as a DLL, this macro expands to `__declspec(dllexport)` @@ -665,18 +704,24 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #define ABSL_DLL #endif // defined(_MSC_VER) +#if defined(_MSC_VER) +#if defined(ABSL_BUILD_TEST_DLL) +#define ABSL_TEST_DLL __declspec(dllexport) +#elif defined(ABSL_CONSUME_TEST_DLL) +#define ABSL_TEST_DLL __declspec(dllimport) +#else +#define ABSL_TEST_DLL +#endif +#else +#define ABSL_TEST_DLL +#endif // defined(_MSC_VER) + // ABSL_HAVE_MEMORY_SANITIZER // // MemorySanitizer (MSan) is a detector of uninitialized reads. It consists of // a compiler instrumentation module and a run-time library. #ifdef ABSL_HAVE_MEMORY_SANITIZER #error "ABSL_HAVE_MEMORY_SANITIZER cannot be directly set." -#elif defined(MEMORY_SANITIZER) -// The MEMORY_SANITIZER macro is deprecated but we will continue to honor it -// for now. -#define ABSL_HAVE_MEMORY_SANITIZER 1 -#elif defined(__SANITIZE_MEMORY__) -#define ABSL_HAVE_MEMORY_SANITIZER 1 #elif !defined(__native_client__) && ABSL_HAVE_FEATURE(memory_sanitizer) #define ABSL_HAVE_MEMORY_SANITIZER 1 #endif @@ -686,10 +731,6 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || // ThreadSanitizer (TSan) is a fast data race detector. #ifdef ABSL_HAVE_THREAD_SANITIZER #error "ABSL_HAVE_THREAD_SANITIZER cannot be directly set." -#elif defined(THREAD_SANITIZER) -// The THREAD_SANITIZER macro is deprecated but we will continue to honor it -// for now. -#define ABSL_HAVE_THREAD_SANITIZER 1 #elif defined(__SANITIZE_THREAD__) #define ABSL_HAVE_THREAD_SANITIZER 1 #elif ABSL_HAVE_FEATURE(thread_sanitizer) @@ -701,14 +742,196 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || // AddressSanitizer (ASan) is a fast memory error detector. #ifdef ABSL_HAVE_ADDRESS_SANITIZER #error "ABSL_HAVE_ADDRESS_SANITIZER cannot be directly set." -#elif defined(ADDRESS_SANITIZER) -// The ADDRESS_SANITIZER macro is deprecated but we will continue to honor it -// for now. -#define ABSL_HAVE_ADDRESS_SANITIZER 1 #elif defined(__SANITIZE_ADDRESS__) #define ABSL_HAVE_ADDRESS_SANITIZER 1 #elif ABSL_HAVE_FEATURE(address_sanitizer) #define ABSL_HAVE_ADDRESS_SANITIZER 1 #endif +// ABSL_HAVE_HWADDRESS_SANITIZER +// +// Hardware-Assisted AddressSanitizer (or HWASAN) is even faster than asan +// memory error detector which can use CPU features like ARM TBI, Intel LAM or +// AMD UAI. +#ifdef ABSL_HAVE_HWADDRESS_SANITIZER +#error "ABSL_HAVE_HWADDRESS_SANITIZER cannot be directly set." +#elif defined(__SANITIZE_HWADDRESS__) +#define ABSL_HAVE_HWADDRESS_SANITIZER 1 +#elif ABSL_HAVE_FEATURE(hwaddress_sanitizer) +#define ABSL_HAVE_HWADDRESS_SANITIZER 1 +#endif + +// ABSL_HAVE_DATAFLOW_SANITIZER +// +// Dataflow Sanitizer (or DFSAN) is a generalised dynamic data flow analysis. +#ifdef ABSL_HAVE_DATAFLOW_SANITIZER +#error "ABSL_HAVE_DATAFLOW_SANITIZER cannot be directly set." +#elif defined(DATAFLOW_SANITIZER) +// GCC provides no method for detecting the presence of the standalone +// DataFlowSanitizer (-fsanitize=dataflow), so GCC users of -fsanitize=dataflow +// should also use -DDATAFLOW_SANITIZER. +#define ABSL_HAVE_DATAFLOW_SANITIZER 1 +#elif ABSL_HAVE_FEATURE(dataflow_sanitizer) +#define ABSL_HAVE_DATAFLOW_SANITIZER 1 +#endif + +// ABSL_HAVE_LEAK_SANITIZER +// +// LeakSanitizer (or lsan) is a detector of memory leaks. +// https://clang.llvm.org/docs/LeakSanitizer.html +// https://github.com/google/sanitizers/wiki/AddressSanitizerLeakSanitizer +// +// The macro ABSL_HAVE_LEAK_SANITIZER can be used to detect at compile-time +// whether the LeakSanitizer is potentially available. However, just because the +// LeakSanitizer is available does not mean it is active. Use the +// always-available run-time interface in //absl/debugging/leak_check.h for +// interacting with LeakSanitizer. +#ifdef ABSL_HAVE_LEAK_SANITIZER +#error "ABSL_HAVE_LEAK_SANITIZER cannot be directly set." +#elif defined(LEAK_SANITIZER) +// GCC provides no method for detecting the presence of the standalone +// LeakSanitizer (-fsanitize=leak), so GCC users of -fsanitize=leak should also +// use -DLEAK_SANITIZER. +#define ABSL_HAVE_LEAK_SANITIZER 1 +// Clang standalone LeakSanitizer (-fsanitize=leak) +#elif ABSL_HAVE_FEATURE(leak_sanitizer) +#define ABSL_HAVE_LEAK_SANITIZER 1 +#elif defined(ABSL_HAVE_ADDRESS_SANITIZER) +// GCC or Clang using the LeakSanitizer integrated into AddressSanitizer. +#define ABSL_HAVE_LEAK_SANITIZER 1 +#endif + +// ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION +// +// Class template argument deduction is a language feature added in C++17. +#ifdef ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION +#error "ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION cannot be directly set." +#elif defined(__cpp_deduction_guides) +#define ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION 1 +#endif + +// ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL +// +// Prior to C++17, static constexpr variables defined in classes required a +// separate definition outside of the class body, for example: +// +// class Foo { +// static constexpr int kBar = 0; +// }; +// constexpr int Foo::kBar; +// +// In C++17, these variables defined in classes are considered inline variables, +// and the extra declaration is redundant. Since some compilers warn on the +// extra declarations, ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL can be used +// conditionally ignore them: +// +// #ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL +// constexpr int Foo::kBar; +// #endif +#if defined(ABSL_INTERNAL_CPLUSPLUS_LANG) && \ + ABSL_INTERNAL_CPLUSPLUS_LANG < 201703L +#define ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL 1 +#endif + +// `ABSL_INTERNAL_HAS_RTTI` determines whether abseil is being compiled with +// RTTI support. +#ifdef ABSL_INTERNAL_HAS_RTTI +#error ABSL_INTERNAL_HAS_RTTI cannot be directly set +#elif (defined(__GNUC__) && defined(__GXX_RTTI)) || \ + (defined(_MSC_VER) && defined(_CPPRTTI)) || \ + (!defined(__GNUC__) && !defined(_MSC_VER)) +#define ABSL_INTERNAL_HAS_RTTI 1 +#endif // !defined(__GNUC__) || defined(__GXX_RTTI) + +// ABSL_INTERNAL_HAVE_SSE is used for compile-time detection of SSE support. +// See https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html for an overview of +// which architectures support the various x86 instruction sets. +#ifdef ABSL_INTERNAL_HAVE_SSE +#error ABSL_INTERNAL_HAVE_SSE cannot be directly set +#elif defined(__SSE__) +#define ABSL_INTERNAL_HAVE_SSE 1 +#elif (defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP >= 1)) && \ + !defined(_M_ARM64EC) +// MSVC only defines _M_IX86_FP for x86 32-bit code, and _M_IX86_FP >= 1 +// indicates that at least SSE was targeted with the /arch:SSE option. +// All x86-64 processors support SSE, so support can be assumed. +// https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros +#define ABSL_INTERNAL_HAVE_SSE 1 +#endif + +// ABSL_INTERNAL_HAVE_SSE2 is used for compile-time detection of SSE2 support. +// See https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html for an overview of +// which architectures support the various x86 instruction sets. +#ifdef ABSL_INTERNAL_HAVE_SSE2 +#error ABSL_INTERNAL_HAVE_SSE2 cannot be directly set +#elif defined(__SSE2__) +#define ABSL_INTERNAL_HAVE_SSE2 1 +#elif (defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP >= 2)) && \ + !defined(_M_ARM64EC) +// MSVC only defines _M_IX86_FP for x86 32-bit code, and _M_IX86_FP >= 2 +// indicates that at least SSE2 was targeted with the /arch:SSE2 option. +// All x86-64 processors support SSE2, so support can be assumed. +// https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros +#define ABSL_INTERNAL_HAVE_SSE2 1 +#endif + +// ABSL_INTERNAL_HAVE_SSSE3 is used for compile-time detection of SSSE3 support. +// See https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html for an overview of +// which architectures support the various x86 instruction sets. +// +// MSVC does not have a mode that targets SSSE3 at compile-time. To use SSSE3 +// with MSVC requires either assuming that the code will only every run on CPUs +// that support SSSE3, otherwise __cpuid() can be used to detect support at +// runtime and fallback to a non-SSSE3 implementation when SSSE3 is unsupported +// by the CPU. +#ifdef ABSL_INTERNAL_HAVE_SSSE3 +#error ABSL_INTERNAL_HAVE_SSSE3 cannot be directly set +#elif defined(__SSSE3__) +#define ABSL_INTERNAL_HAVE_SSSE3 1 +#endif + +// ABSL_INTERNAL_HAVE_ARM_NEON is used for compile-time detection of NEON (ARM +// SIMD). +// +// If __CUDA_ARCH__ is defined, then we are compiling CUDA code in device mode. +// In device mode, NEON intrinsics are not available, regardless of host +// platform. +// https://llvm.org/docs/CompileCudaWithLLVM.html#detecting-clang-vs-nvcc-from-code +#ifdef ABSL_INTERNAL_HAVE_ARM_NEON +#error ABSL_INTERNAL_HAVE_ARM_NEON cannot be directly set +#elif defined(__ARM_NEON) && !defined(__CUDA_ARCH__) +#define ABSL_INTERNAL_HAVE_ARM_NEON 1 +#endif + +// ABSL_HAVE_CONSTANT_EVALUATED is used for compile-time detection of +// constant evaluation support through `absl::is_constant_evaluated`. +#ifdef ABSL_HAVE_CONSTANT_EVALUATED +#error ABSL_HAVE_CONSTANT_EVALUATED cannot be directly set +#endif +#ifdef __cpp_lib_is_constant_evaluated +#define ABSL_HAVE_CONSTANT_EVALUATED 1 +#elif ABSL_HAVE_BUILTIN(__builtin_is_constant_evaluated) +#define ABSL_HAVE_CONSTANT_EVALUATED 1 +#endif + +// ABSL_INTERNAL_EMSCRIPTEN_VERSION combines Emscripten's three version macros +// into an integer that can be compared against. +#ifdef ABSL_INTERNAL_EMSCRIPTEN_VERSION +#error ABSL_INTERNAL_EMSCRIPTEN_VERSION cannot be directly set +#endif +#ifdef __EMSCRIPTEN__ +#include +#ifdef __EMSCRIPTEN_major__ +#if __EMSCRIPTEN_minor__ >= 1000 +#error __EMSCRIPTEN_minor__ is too big to fit in ABSL_INTERNAL_EMSCRIPTEN_VERSION +#endif +#if __EMSCRIPTEN_tiny__ >= 1000 +#error __EMSCRIPTEN_tiny__ is too big to fit in ABSL_INTERNAL_EMSCRIPTEN_VERSION +#endif +#define ABSL_INTERNAL_EMSCRIPTEN_VERSION \ + ((__EMSCRIPTEN_major__)*1000000 + (__EMSCRIPTEN_minor__)*1000 + \ + (__EMSCRIPTEN_tiny__)) +#endif +#endif + #endif // ABSL_BASE_CONFIG_H_ diff --git a/Siv3D/include/ThirdParty/absl/base/dynamic_annotations.h b/Siv3D/include/ThirdParty/absl/base/dynamic_annotations.h index 545f8cbc91..7ba8912ea6 100644 --- a/Siv3D/include/ThirdParty/absl/base/dynamic_annotations.h +++ b/Siv3D/include/ThirdParty/absl/base/dynamic_annotations.h @@ -46,6 +46,7 @@ #define ABSL_BASE_DYNAMIC_ANNOTATIONS_H_ #include +#include #include "absl/base/attributes.h" #include "absl/base/config.h" @@ -53,6 +54,10 @@ #include "absl/base/macros.h" #endif +#ifdef ABSL_HAVE_HWADDRESS_SANITIZER +#include +#endif + // TODO(rogeeff): Remove after the backward compatibility period. #include "absl/base/internal/dynamic_annotations.h" // IWYU pragma: export @@ -110,6 +115,9 @@ // Define race annotations. #if ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED == 1 +// Some of the symbols used in this section (e.g. AnnotateBenignRaceSized) are +// defined by the compiler-based sanitizer implementation, not by the Abseil +// library. Therefore they do not use ABSL_INTERNAL_C_SYMBOL. // ------------------------------------------------------------- // Annotations that suppress errors. It is usually better to express the @@ -286,17 +294,22 @@ ABSL_INTERNAL_END_EXTERN_C // Define IGNORE_READS_BEGIN/_END annotations. #if ABSL_INTERNAL_READS_ANNOTATIONS_ENABLED == 1 +// Some of the symbols used in this section (e.g. AnnotateIgnoreReadsBegin) are +// defined by the compiler-based implementation, not by the Abseil +// library. Therefore they do not use ABSL_INTERNAL_C_SYMBOL. // Request the analysis tool to ignore all reads in the current thread until // ABSL_ANNOTATE_IGNORE_READS_END is called. Useful to ignore intentional racey // reads, while still checking other reads and all writes. // See also ABSL_ANNOTATE_UNPROTECTED_READ. -#define ABSL_ANNOTATE_IGNORE_READS_BEGIN() \ - ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreReadsBegin)(__FILE__, __LINE__) +#define ABSL_ANNOTATE_IGNORE_READS_BEGIN() \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreReadsBegin) \ + (__FILE__, __LINE__) // Stop ignoring reads. -#define ABSL_ANNOTATE_IGNORE_READS_END() \ - ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreReadsEnd)(__FILE__, __LINE__) +#define ABSL_ANNOTATE_IGNORE_READS_END() \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreReadsEnd) \ + (__FILE__, __LINE__) // Function prototypes of annotations provided by the compiler-based sanitizer // implementation. @@ -316,16 +329,22 @@ ABSL_INTERNAL_END_EXTERN_C // TODO(delesley) -- The exclusive lock here ignores writes as well, but // allows IGNORE_READS_AND_WRITES to work properly. -#define ABSL_ANNOTATE_IGNORE_READS_BEGIN() \ - ABSL_INTERNAL_GLOBAL_SCOPED(AbslInternalAnnotateIgnoreReadsBegin)() +#define ABSL_ANNOTATE_IGNORE_READS_BEGIN() \ + ABSL_INTERNAL_GLOBAL_SCOPED( \ + ABSL_INTERNAL_C_SYMBOL(AbslInternalAnnotateIgnoreReadsBegin)) \ + () -#define ABSL_ANNOTATE_IGNORE_READS_END() \ - ABSL_INTERNAL_GLOBAL_SCOPED(AbslInternalAnnotateIgnoreReadsEnd)() +#define ABSL_ANNOTATE_IGNORE_READS_END() \ + ABSL_INTERNAL_GLOBAL_SCOPED( \ + ABSL_INTERNAL_C_SYMBOL(AbslInternalAnnotateIgnoreReadsEnd)) \ + () -ABSL_INTERNAL_STATIC_INLINE void AbslInternalAnnotateIgnoreReadsBegin() +ABSL_INTERNAL_STATIC_INLINE void ABSL_INTERNAL_C_SYMBOL( + AbslInternalAnnotateIgnoreReadsBegin)() ABSL_INTERNAL_IGNORE_READS_BEGIN_ATTRIBUTE {} -ABSL_INTERNAL_STATIC_INLINE void AbslInternalAnnotateIgnoreReadsEnd() +ABSL_INTERNAL_STATIC_INLINE void ABSL_INTERNAL_C_SYMBOL( + AbslInternalAnnotateIgnoreReadsEnd)() ABSL_INTERNAL_IGNORE_READS_END_ATTRIBUTE {} #else @@ -419,31 +438,6 @@ ABSL_NAMESPACE_END #endif -#ifdef __cplusplus -#ifdef ABSL_HAVE_THREAD_SANITIZER -ABSL_INTERNAL_BEGIN_EXTERN_C -int RunningOnValgrind(); -double ValgrindSlowdown(); -ABSL_INTERNAL_END_EXTERN_C -#else -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace base_internal { -ABSL_DEPRECATED( - "Don't use this interface. It is misleading and is being deleted.") -ABSL_ATTRIBUTE_ALWAYS_INLINE inline int RunningOnValgrind() { return 0; } -ABSL_DEPRECATED( - "Don't use this interface. It is misleading and is being deleted.") -ABSL_ATTRIBUTE_ALWAYS_INLINE inline double ValgrindSlowdown() { return 1.0; } -} // namespace base_internal -ABSL_NAMESPACE_END -} // namespace absl - -using absl::base_internal::RunningOnValgrind; -using absl::base_internal::ValgrindSlowdown; -#endif -#endif - // ------------------------------------------------------------------------- // Address sanitizer annotations @@ -457,7 +451,7 @@ using absl::base_internal::ValgrindSlowdown; __sanitizer_annotate_contiguous_container(beg, end, old_mid, new_mid) #define ABSL_ADDRESS_SANITIZER_REDZONE(name) \ struct { \ - char x[8] __attribute__((aligned(8))); \ + alignas(8) char x[8]; \ } name #else @@ -467,6 +461,26 @@ using absl::base_internal::ValgrindSlowdown; #endif // ABSL_HAVE_ADDRESS_SANITIZER +// ------------------------------------------------------------------------- +// HWAddress sanitizer annotations + +#ifdef __cplusplus +namespace absl { +#ifdef ABSL_HAVE_HWADDRESS_SANITIZER +// Under HWASAN changes the tag of the pointer. +template +T* HwasanTagPointer(T* ptr, uintptr_t tag) { + return reinterpret_cast(__hwasan_tag_pointer(ptr, tag)); +} +#else +template +T* HwasanTagPointer(T* ptr, uintptr_t) { + return ptr; +} +#endif +} // namespace absl +#endif + // ------------------------------------------------------------------------- // Undefine the macros intended only for this file. diff --git a/Siv3D/include/ThirdParty/absl/base/internal/atomic_hook_test_helper.h b/Siv3D/include/ThirdParty/absl/base/internal/atomic_hook_test_helper.h index 3e72b4977d..c72015ef96 100644 --- a/Siv3D/include/ThirdParty/absl/base/internal/atomic_hook_test_helper.h +++ b/Siv3D/include/ThirdParty/absl/base/internal/atomic_hook_test_helper.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef ABSL_BASE_ATOMIC_HOOK_TEST_HELPER_H_ -#define ABSL_BASE_ATOMIC_HOOK_TEST_HELPER_H_ +#ifndef ABSL_BASE_INTERNAL_ATOMIC_HOOK_TEST_HELPER_H_ +#define ABSL_BASE_INTERNAL_ATOMIC_HOOK_TEST_HELPER_H_ #include "absl/base/internal/atomic_hook.h" @@ -31,4 +31,4 @@ void RegisterFunc(VoidF func); ABSL_NAMESPACE_END } // namespace absl -#endif // ABSL_BASE_ATOMIC_HOOK_TEST_HELPER_H_ +#endif // ABSL_BASE_INTERNAL_ATOMIC_HOOK_TEST_HELPER_H_ diff --git a/Siv3D/include/ThirdParty/absl/base/internal/bits.h b/Siv3D/include/ThirdParty/absl/base/internal/bits.h deleted file mode 100644 index 81648e2c39..0000000000 --- a/Siv3D/include/ThirdParty/absl/base/internal/bits.h +++ /dev/null @@ -1,219 +0,0 @@ -// Copyright 2018 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef ABSL_BASE_INTERNAL_BITS_H_ -#define ABSL_BASE_INTERNAL_BITS_H_ - -// This file contains bitwise ops which are implementation details of various -// absl libraries. - -#include - -#include "absl/base/config.h" - -// Clang on Windows has __builtin_clzll; otherwise we need to use the -// windows intrinsic functions. -#if defined(_MSC_VER) && !defined(__clang__) -#include -#if defined(_M_X64) -#pragma intrinsic(_BitScanReverse64) -#pragma intrinsic(_BitScanForward64) -#endif -#pragma intrinsic(_BitScanReverse) -#pragma intrinsic(_BitScanForward) -#endif - -#include "absl/base/attributes.h" - -#if defined(_MSC_VER) && !defined(__clang__) -// We can achieve something similar to attribute((always_inline)) with MSVC by -// using the __forceinline keyword, however this is not perfect. MSVC is -// much less aggressive about inlining, and even with the __forceinline keyword. -#define ABSL_BASE_INTERNAL_FORCEINLINE __forceinline -#else -// Use default attribute inline. -#define ABSL_BASE_INTERNAL_FORCEINLINE inline ABSL_ATTRIBUTE_ALWAYS_INLINE -#endif - - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace base_internal { - -ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros64Slow(uint64_t n) { - int zeroes = 60; - if (n >> 32) { - zeroes -= 32; - n >>= 32; - } - if (n >> 16) { - zeroes -= 16; - n >>= 16; - } - if (n >> 8) { - zeroes -= 8; - n >>= 8; - } - if (n >> 4) { - zeroes -= 4; - n >>= 4; - } - return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[n] + zeroes; -} - -ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros64(uint64_t n) { -#if defined(_MSC_VER) && !defined(__clang__) && defined(_M_X64) - // MSVC does not have __buitin_clzll. Use _BitScanReverse64. - unsigned long result = 0; // NOLINT(runtime/int) - if (_BitScanReverse64(&result, n)) { - return 63 - result; - } - return 64; -#elif defined(_MSC_VER) && !defined(__clang__) - // MSVC does not have __buitin_clzll. Compose two calls to _BitScanReverse - unsigned long result = 0; // NOLINT(runtime/int) - if ((n >> 32) && - _BitScanReverse(&result, static_cast(n >> 32))) { - return 31 - result; - } - if (_BitScanReverse(&result, static_cast(n))) { - return 63 - result; - } - return 64; -#elif defined(__GNUC__) || defined(__clang__) - // Use __builtin_clzll, which uses the following instructions: - // x86: bsr - // ARM64: clz - // PPC: cntlzd - static_assert(sizeof(unsigned long long) == sizeof(n), // NOLINT(runtime/int) - "__builtin_clzll does not take 64-bit arg"); - - // Handle 0 as a special case because __builtin_clzll(0) is undefined. - if (n == 0) { - return 64; - } - return __builtin_clzll(n); -#else - return CountLeadingZeros64Slow(n); -#endif -} - -ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros32Slow(uint64_t n) { - int zeroes = 28; - if (n >> 16) { - zeroes -= 16; - n >>= 16; - } - if (n >> 8) { - zeroes -= 8; - n >>= 8; - } - if (n >> 4) { - zeroes -= 4; - n >>= 4; - } - return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[n] + zeroes; -} - -ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros32(uint32_t n) { -#if defined(_MSC_VER) && !defined(__clang__) - unsigned long result = 0; // NOLINT(runtime/int) - if (_BitScanReverse(&result, n)) { - return 31 - result; - } - return 32; -#elif defined(__GNUC__) || defined(__clang__) - // Use __builtin_clz, which uses the following instructions: - // x86: bsr - // ARM64: clz - // PPC: cntlzd - static_assert(sizeof(int) == sizeof(n), - "__builtin_clz does not take 32-bit arg"); - - // Handle 0 as a special case because __builtin_clz(0) is undefined. - if (n == 0) { - return 32; - } - return __builtin_clz(n); -#else - return CountLeadingZeros32Slow(n); -#endif -} - -ABSL_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero64Slow(uint64_t n) { - int c = 63; - n &= ~n + 1; - if (n & 0x00000000FFFFFFFF) c -= 32; - if (n & 0x0000FFFF0000FFFF) c -= 16; - if (n & 0x00FF00FF00FF00FF) c -= 8; - if (n & 0x0F0F0F0F0F0F0F0F) c -= 4; - if (n & 0x3333333333333333) c -= 2; - if (n & 0x5555555555555555) c -= 1; - return c; -} - -ABSL_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero64(uint64_t n) { -#if defined(_MSC_VER) && !defined(__clang__) && defined(_M_X64) - unsigned long result = 0; // NOLINT(runtime/int) - _BitScanForward64(&result, n); - return result; -#elif defined(_MSC_VER) && !defined(__clang__) - unsigned long result = 0; // NOLINT(runtime/int) - if (static_cast(n) == 0) { - _BitScanForward(&result, static_cast(n >> 32)); - return result + 32; - } - _BitScanForward(&result, static_cast(n)); - return result; -#elif defined(__GNUC__) || defined(__clang__) - static_assert(sizeof(unsigned long long) == sizeof(n), // NOLINT(runtime/int) - "__builtin_ctzll does not take 64-bit arg"); - return __builtin_ctzll(n); -#else - return CountTrailingZerosNonZero64Slow(n); -#endif -} - -ABSL_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero32Slow(uint32_t n) { - int c = 31; - n &= ~n + 1; - if (n & 0x0000FFFF) c -= 16; - if (n & 0x00FF00FF) c -= 8; - if (n & 0x0F0F0F0F) c -= 4; - if (n & 0x33333333) c -= 2; - if (n & 0x55555555) c -= 1; - return c; -} - -ABSL_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero32(uint32_t n) { -#if defined(_MSC_VER) && !defined(__clang__) - unsigned long result = 0; // NOLINT(runtime/int) - _BitScanForward(&result, n); - return result; -#elif defined(__GNUC__) || defined(__clang__) - static_assert(sizeof(int) == sizeof(n), - "__builtin_ctz does not take 32-bit arg"); - return __builtin_ctz(n); -#else - return CountTrailingZerosNonZero32Slow(n); -#endif -} - -#undef ABSL_BASE_INTERNAL_FORCEINLINE - -} // namespace base_internal -ABSL_NAMESPACE_END -} // namespace absl - -#endif // ABSL_BASE_INTERNAL_BITS_H_ diff --git a/Siv3D/include/ThirdParty/absl/base/internal/cycleclock.h b/Siv3D/include/ThirdParty/absl/base/internal/cycleclock.h index a18b584445..cbfdf57998 100644 --- a/Siv3D/include/ThirdParty/absl/base/internal/cycleclock.h +++ b/Siv3D/include/ThirdParty/absl/base/internal/cycleclock.h @@ -42,14 +42,20 @@ #ifndef ABSL_BASE_INTERNAL_CYCLECLOCK_H_ #define ABSL_BASE_INTERNAL_CYCLECLOCK_H_ +#include #include +#include "absl/base/attributes.h" #include "absl/base/config.h" +#include "absl/base/internal/cycleclock_config.h" +#include "absl/base/internal/unscaledcycleclock.h" namespace absl { ABSL_NAMESPACE_BEGIN namespace base_internal { +using CycleClockSourceFunc = int64_t (*)(); + // ----------------------------------------------------------------------------- // CycleClock // ----------------------------------------------------------------------------- @@ -68,12 +74,21 @@ class CycleClock { static double Frequency(); private: +#if ABSL_USE_UNSCALED_CYCLECLOCK + static CycleClockSourceFunc LoadCycleClockSource(); + + static constexpr int32_t kShift = kCycleClockShift; + static constexpr double kFrequencyScale = kCycleClockFrequencyScale; + + ABSL_CONST_INIT static std::atomic cycle_clock_source_; +#endif // ABSL_USE_UNSCALED_CYCLECLOC + CycleClock() = delete; // no instances CycleClock(const CycleClock&) = delete; CycleClock& operator=(const CycleClock&) = delete; -}; -using CycleClockSourceFunc = int64_t (*)(); + friend class CycleClockSource; +}; class CycleClockSource { private: @@ -87,6 +102,41 @@ class CycleClockSource { static void Register(CycleClockSourceFunc source); }; +#if ABSL_USE_UNSCALED_CYCLECLOCK + +inline CycleClockSourceFunc CycleClock::LoadCycleClockSource() { +#if !defined(__x86_64__) + // Optimize for the common case (no callback) by first doing a relaxed load; + // this is significantly faster on non-x86 platforms. + if (cycle_clock_source_.load(std::memory_order_relaxed) == nullptr) { + return nullptr; + } +#endif // !defined(__x86_64__) + + // This corresponds to the store(std::memory_order_release) in + // CycleClockSource::Register, and makes sure that any updates made prior to + // registering the callback are visible to this thread before the callback + // is invoked. + return cycle_clock_source_.load(std::memory_order_acquire); +} + +// Accessing globals in inlined code in Window DLLs is problematic. +#ifndef _WIN32 +inline int64_t CycleClock::Now() { + auto fn = LoadCycleClockSource(); + if (fn == nullptr) { + return base_internal::UnscaledCycleClock::Now() >> kShift; + } + return fn() >> kShift; +} +#endif + +inline double CycleClock::Frequency() { + return kFrequencyScale * base_internal::UnscaledCycleClock::Frequency(); +} + +#endif // ABSL_USE_UNSCALED_CYCLECLOCK + } // namespace base_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/Siv3D/include/ThirdParty/absl/base/internal/cycleclock_config.h b/Siv3D/include/ThirdParty/absl/base/internal/cycleclock_config.h new file mode 100644 index 0000000000..191112b58e --- /dev/null +++ b/Siv3D/include/ThirdParty/absl/base/internal/cycleclock_config.h @@ -0,0 +1,55 @@ +// Copyright 2022 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_BASE_INTERNAL_CYCLECLOCK_CONFIG_H_ +#define ABSL_BASE_INTERNAL_CYCLECLOCK_CONFIG_H_ + +#include + +#include "absl/base/config.h" +#include "absl/base/internal/inline_variable.h" +#include "absl/base/internal/unscaledcycleclock_config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +#if ABSL_USE_UNSCALED_CYCLECLOCK +#ifdef NDEBUG +#ifdef ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY +// Not debug mode and the UnscaledCycleClock frequency is the CPU +// frequency. Scale the CycleClock to prevent overflow if someone +// tries to represent the time as cycles since the Unix epoch. +ABSL_INTERNAL_INLINE_CONSTEXPR(int32_t, kCycleClockShift, 1); +#else +// Not debug mode and the UnscaledCycleClock isn't operating at the +// raw CPU frequency. There is no need to do any scaling, so don't +// needlessly sacrifice precision. +ABSL_INTERNAL_INLINE_CONSTEXPR(int32_t, kCycleClockShift, 0); +#endif +#else // NDEBUG +// In debug mode use a different shift to discourage depending on a +// particular shift value. +ABSL_INTERNAL_INLINE_CONSTEXPR(int32_t, kCycleClockShift, 2); +#endif // NDEBUG + +ABSL_INTERNAL_INLINE_CONSTEXPR(double, kCycleClockFrequencyScale, + 1.0 / (1 << kCycleClockShift)); +#endif // ABSL_USE_UNSCALED_CYCLECLOC + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_CYCLECLOCK_CONFIG_H_ diff --git a/Siv3D/include/ThirdParty/absl/base/internal/direct_mmap.h b/Siv3D/include/ThirdParty/absl/base/internal/direct_mmap.h index 16accf0966..1beb2ee4e5 100644 --- a/Siv3D/include/ThirdParty/absl/base/internal/direct_mmap.h +++ b/Siv3D/include/ThirdParty/absl/base/internal/direct_mmap.h @@ -20,7 +20,7 @@ #include "absl/base/config.h" -#if ABSL_HAVE_MMAP +#ifdef ABSL_HAVE_MMAP #include @@ -41,13 +41,13 @@ #ifdef __mips__ // Include definitions of the ABI currently in use. -#ifdef __BIONIC__ +#if defined(__BIONIC__) || !defined(__GLIBC__) // Android doesn't have sgidefs.h, but does have asm/sgidefs.h, which has the // definitions we need. #include #else #include -#endif // __BIONIC__ +#endif // __BIONIC__ || !__GLIBC__ #endif // __mips__ // SYS_mmap and SYS_munmap are not defined in Android. @@ -72,12 +72,15 @@ namespace base_internal { // Platform specific logic extracted from // https://chromium.googlesource.com/linux-syscall-support/+/master/linux_syscall_support.h inline void* DirectMmap(void* start, size_t length, int prot, int flags, int fd, - off64_t offset) noexcept { + off_t offset) noexcept { #if defined(__i386__) || defined(__ARM_ARCH_3__) || defined(__ARM_EABI__) || \ + defined(__m68k__) || defined(__sh__) || \ + (defined(__hppa__) && !defined(__LP64__)) || \ (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) || \ (defined(__PPC__) && !defined(__PPC64__)) || \ (defined(__riscv) && __riscv_xlen == 32) || \ - (defined(__s390__) && !defined(__s390x__)) + (defined(__s390__) && !defined(__s390x__)) || \ + (defined(__sparc__) && !defined(__arch64__)) // On these architectures, implement mmap with mmap2. static int pagesize = 0; if (pagesize == 0) { @@ -94,11 +97,12 @@ inline void* DirectMmap(void* start, size_t length, int prot, int flags, int fd, #ifdef __BIONIC__ // SYS_mmap2 has problems on Android API level <= 16. // Workaround by invoking __mmap2() instead. - return __mmap2(start, length, prot, flags, fd, offset / pagesize); + return __mmap2(start, length, prot, flags, fd, + static_cast(offset / pagesize)); #else return reinterpret_cast( syscall(SYS_mmap2, start, length, prot, flags, fd, - static_cast(offset / pagesize))); + static_cast(offset / pagesize))); // NOLINT #endif #elif defined(__s390x__) // On s390x, mmap() arguments are passed in memory. diff --git a/Siv3D/include/ThirdParty/absl/base/internal/endian.h b/Siv3D/include/ThirdParty/absl/base/internal/endian.h index 9677530e8d..50747d75ec 100644 --- a/Siv3D/include/ThirdParty/absl/base/internal/endian.h +++ b/Siv3D/include/ThirdParty/absl/base/internal/endian.h @@ -16,16 +16,10 @@ #ifndef ABSL_BASE_INTERNAL_ENDIAN_H_ #define ABSL_BASE_INTERNAL_ENDIAN_H_ -// The following guarantees declaration of the byte swap functions -#ifdef _MSC_VER -#include // NOLINT(build/include) -#elif defined(__FreeBSD__) -#include -#elif defined(__GLIBC__) -#include // IWYU pragma: export -#endif - #include +#include + +#include "absl/base/casts.h" #include "absl/base/config.h" #include "absl/base/internal/unaligned_access.h" #include "absl/base/port.h" @@ -33,47 +27,11 @@ namespace absl { ABSL_NAMESPACE_BEGIN -// Use compiler byte-swapping intrinsics if they are available. 32-bit -// and 64-bit versions are available in Clang and GCC as of GCC 4.3.0. -// The 16-bit version is available in Clang and GCC only as of GCC 4.8.0. -// For simplicity, we enable them all only for GCC 4.8.0 or later. -#if defined(__clang__) || \ - (defined(__GNUC__) && \ - ((__GNUC__ == 4 && __GNUC_MINOR__ >= 8) || __GNUC__ >= 5)) inline uint64_t gbswap_64(uint64_t host_int) { +#if ABSL_HAVE_BUILTIN(__builtin_bswap64) || defined(__GNUC__) return __builtin_bswap64(host_int); -} -inline uint32_t gbswap_32(uint32_t host_int) { - return __builtin_bswap32(host_int); -} -inline uint16_t gbswap_16(uint16_t host_int) { - return __builtin_bswap16(host_int); -} - #elif defined(_MSC_VER) -inline uint64_t gbswap_64(uint64_t host_int) { return _byteswap_uint64(host_int); -} -inline uint32_t gbswap_32(uint32_t host_int) { - return _byteswap_ulong(host_int); -} -inline uint16_t gbswap_16(uint16_t host_int) { - return _byteswap_ushort(host_int); -} - -#else -inline uint64_t gbswap_64(uint64_t host_int) { -#if defined(__GNUC__) && defined(__x86_64__) && !defined(__APPLE__) - // Adapted from /usr/include/byteswap.h. Not available on Mac. - if (__builtin_constant_p(host_int)) { - return __bswap_constant_64(host_int); - } else { - uint64_t result; - __asm__("bswap %0" : "=r"(result) : "0"(host_int)); - return result; - } -#elif defined(__GLIBC__) - return bswap_64(host_int); #else return (((host_int & uint64_t{0xFF}) << 56) | ((host_int & uint64_t{0xFF00}) << 40) | @@ -83,12 +41,14 @@ inline uint64_t gbswap_64(uint64_t host_int) { ((host_int & uint64_t{0xFF0000000000}) >> 24) | ((host_int & uint64_t{0xFF000000000000}) >> 40) | ((host_int & uint64_t{0xFF00000000000000}) >> 56)); -#endif // bswap_64 +#endif } inline uint32_t gbswap_32(uint32_t host_int) { -#if defined(__GLIBC__) - return bswap_32(host_int); +#if ABSL_HAVE_BUILTIN(__builtin_bswap32) || defined(__GNUC__) + return __builtin_bswap32(host_int); +#elif defined(_MSC_VER) + return _byteswap_ulong(host_int); #else return (((host_int & uint32_t{0xFF}) << 24) | ((host_int & uint32_t{0xFF00}) << 8) | @@ -98,33 +58,29 @@ inline uint32_t gbswap_32(uint32_t host_int) { } inline uint16_t gbswap_16(uint16_t host_int) { -#if defined(__GLIBC__) - return bswap_16(host_int); +#if ABSL_HAVE_BUILTIN(__builtin_bswap16) || defined(__GNUC__) + return __builtin_bswap16(host_int); +#elif defined(_MSC_VER) + return _byteswap_ushort(host_int); #else return (((host_int & uint16_t{0xFF}) << 8) | ((host_int & uint16_t{0xFF00}) >> 8)); #endif } -#endif // intrinsics available - #ifdef ABSL_IS_LITTLE_ENDIAN -// Definitions for ntohl etc. that don't require us to include -// netinet/in.h. We wrap gbswap_32 and gbswap_16 in functions rather -// than just #defining them because in debug mode, gcc doesn't -// correctly handle the (rather involved) definitions of bswap_32. -// gcc guarantees that inline functions are as fast as macros, so -// this isn't a performance hit. +// Portable definitions for htonl (host-to-network) and friends on little-endian +// architectures. inline uint16_t ghtons(uint16_t x) { return gbswap_16(x); } inline uint32_t ghtonl(uint32_t x) { return gbswap_32(x); } inline uint64_t ghtonll(uint64_t x) { return gbswap_64(x); } #elif defined ABSL_IS_BIG_ENDIAN -// These definitions are simpler on big-endian machines -// These are functions instead of macros to avoid self-assignment warnings -// on calls such as "i = ghtnol(i);". This also provides type checking. +// Portable definitions for htonl (host-to-network) etc on big-endian +// architectures. These definitions are simpler since the host byte order is the +// same as network byte order. inline uint16_t ghtons(uint16_t x) { return x; } inline uint32_t ghtonl(uint32_t x) { return x; } inline uint64_t ghtonll(uint64_t x) { return x; } @@ -173,6 +129,36 @@ inline constexpr bool IsLittleEndian() { return false; } #endif /* ENDIAN */ +inline uint8_t FromHost(uint8_t x) { return x; } +inline uint16_t FromHost(uint16_t x) { return FromHost16(x); } +inline uint32_t FromHost(uint32_t x) { return FromHost32(x); } +inline uint64_t FromHost(uint64_t x) { return FromHost64(x); } +inline uint8_t ToHost(uint8_t x) { return x; } +inline uint16_t ToHost(uint16_t x) { return ToHost16(x); } +inline uint32_t ToHost(uint32_t x) { return ToHost32(x); } +inline uint64_t ToHost(uint64_t x) { return ToHost64(x); } + +inline int8_t FromHost(int8_t x) { return x; } +inline int16_t FromHost(int16_t x) { + return bit_cast(FromHost16(bit_cast(x))); +} +inline int32_t FromHost(int32_t x) { + return bit_cast(FromHost32(bit_cast(x))); +} +inline int64_t FromHost(int64_t x) { + return bit_cast(FromHost64(bit_cast(x))); +} +inline int8_t ToHost(int8_t x) { return x; } +inline int16_t ToHost(int16_t x) { + return bit_cast(ToHost16(bit_cast(x))); +} +inline int32_t ToHost(int32_t x) { + return bit_cast(ToHost32(bit_cast(x))); +} +inline int64_t ToHost(int64_t x) { + return bit_cast(ToHost64(bit_cast(x))); +} + // Functions to do unaligned loads and stores in little-endian order. inline uint16_t Load16(const void *p) { return ToHost16(ABSL_INTERNAL_UNALIGNED_LOAD16(p)); @@ -233,6 +219,36 @@ inline constexpr bool IsLittleEndian() { return false; } #endif /* ENDIAN */ +inline uint8_t FromHost(uint8_t x) { return x; } +inline uint16_t FromHost(uint16_t x) { return FromHost16(x); } +inline uint32_t FromHost(uint32_t x) { return FromHost32(x); } +inline uint64_t FromHost(uint64_t x) { return FromHost64(x); } +inline uint8_t ToHost(uint8_t x) { return x; } +inline uint16_t ToHost(uint16_t x) { return ToHost16(x); } +inline uint32_t ToHost(uint32_t x) { return ToHost32(x); } +inline uint64_t ToHost(uint64_t x) { return ToHost64(x); } + +inline int8_t FromHost(int8_t x) { return x; } +inline int16_t FromHost(int16_t x) { + return bit_cast(FromHost16(bit_cast(x))); +} +inline int32_t FromHost(int32_t x) { + return bit_cast(FromHost32(bit_cast(x))); +} +inline int64_t FromHost(int64_t x) { + return bit_cast(FromHost64(bit_cast(x))); +} +inline int8_t ToHost(int8_t x) { return x; } +inline int16_t ToHost(int16_t x) { + return bit_cast(ToHost16(bit_cast(x))); +} +inline int32_t ToHost(int32_t x) { + return bit_cast(ToHost32(bit_cast(x))); +} +inline int64_t ToHost(int64_t x) { + return bit_cast(ToHost64(bit_cast(x))); +} + // Functions to do unaligned loads and stores in big-endian order. inline uint16_t Load16(const void *p) { return ToHost16(ABSL_INTERNAL_UNALIGNED_LOAD16(p)); diff --git a/Siv3D/include/ThirdParty/absl/base/internal/exception_safety_testing.h b/Siv3D/include/ThirdParty/absl/base/internal/exception_safety_testing.h index 6ba89d05df..c106154407 100644 --- a/Siv3D/include/ThirdParty/absl/base/internal/exception_safety_testing.h +++ b/Siv3D/include/ThirdParty/absl/base/internal/exception_safety_testing.h @@ -536,7 +536,22 @@ class ThrowingValue : private exceptions_internal::TrackedObject { } // Memory management operators - // Args.. allows us to overload regular and placement new in one shot + static void* operator new(size_t s) noexcept( + IsSpecified(TypeSpec::kNoThrowNew)) { + if (!IsSpecified(TypeSpec::kNoThrowNew)) { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION, true); + } + return ::operator new(s); + } + + static void* operator new[](size_t s) noexcept( + IsSpecified(TypeSpec::kNoThrowNew)) { + if (!IsSpecified(TypeSpec::kNoThrowNew)) { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION, true); + } + return ::operator new[](s); + } + template static void* operator new(size_t s, Args&&... args) noexcept( IsSpecified(TypeSpec::kNoThrowNew)) { @@ -557,12 +572,6 @@ class ThrowingValue : private exceptions_internal::TrackedObject { // Abseil doesn't support throwing overloaded operator delete. These are // provided so a throwing operator-new can clean up after itself. - // - // We provide both regular and templated operator delete because if only the - // templated version is provided as we did with operator new, the compiler has - // no way of knowing which overload of operator delete to call. See - // https://en.cppreference.com/w/cpp/memory/new/operator_delete and - // https://en.cppreference.com/w/cpp/language/delete for the gory details. void operator delete(void* p) noexcept { ::operator delete(p); } template @@ -726,9 +735,8 @@ class ThrowingAllocator : private exceptions_internal::TrackedObject { ThrowingAllocator select_on_container_copy_construction() noexcept( IsSpecified(AllocSpec::kNoThrowAllocate)) { - auto& out = *this; ReadStateAndMaybeThrow(ABSL_PRETTY_FUNCTION); - return out; + return *this; } template @@ -938,7 +946,7 @@ class ExceptionSafetyTest { * `std::unique_ptr operator()() const` where T is the type being tested. * It is used for reliably creating identical T instances to test on. * - * - Operation: The operation object (passsed in via tester.WithOperation(...) + * - Operation: The operation object (passed in via tester.WithOperation(...) * or tester.Test(...)) must be invocable with the signature * `void operator()(T*) const` where T is the type being tested. It is used * for performing steps on a T instance that may throw and that need to be diff --git a/Siv3D/include/ThirdParty/absl/base/internal/exponential_biased.h b/Siv3D/include/ThirdParty/absl/base/internal/exponential_biased.h deleted file mode 100644 index 94f79a3378..0000000000 --- a/Siv3D/include/ThirdParty/absl/base/internal/exponential_biased.h +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright 2019 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef ABSL_BASE_INTERNAL_EXPONENTIAL_BIASED_H_ -#define ABSL_BASE_INTERNAL_EXPONENTIAL_BIASED_H_ - -#include - -#include "absl/base/config.h" -#include "absl/base/macros.h" - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace base_internal { - -// ExponentialBiased provides a small and fast random number generator for a -// rounded exponential distribution. This generator manages very little state, -// and imposes no synchronization overhead. This makes it useful in specialized -// scenarios requiring minimum overhead, such as stride based periodic sampling. -// -// ExponentialBiased provides two closely related functions, GetSkipCount() and -// GetStride(), both returning a rounded integer defining a number of events -// required before some event with a given mean probability occurs. -// -// The distribution is useful to generate a random wait time or some periodic -// event with a given mean probability. For example, if an action is supposed to -// happen on average once every 'N' events, then we can get a random 'stride' -// counting down how long before the event to happen. For example, if we'd want -// to sample one in every 1000 'Frobber' calls, our code could look like this: -// -// Frobber::Frobber() { -// stride_ = exponential_biased_.GetStride(1000); -// } -// -// void Frobber::Frob(int arg) { -// if (--stride == 0) { -// SampleFrob(arg); -// stride_ = exponential_biased_.GetStride(1000); -// } -// ... -// } -// -// The rounding of the return value creates a bias, especially for smaller means -// where the distribution of the fraction is not evenly distributed. We correct -// this bias by tracking the fraction we rounded up or down on each iteration, -// effectively tracking the distance between the cumulative value, and the -// rounded cumulative value. For example, given a mean of 2: -// -// raw = 1.63076, cumulative = 1.63076, rounded = 2, bias = -0.36923 -// raw = 0.14624, cumulative = 1.77701, rounded = 2, bias = 0.14624 -// raw = 4.93194, cumulative = 6.70895, rounded = 7, bias = -0.06805 -// raw = 0.24206, cumulative = 6.95101, rounded = 7, bias = 0.24206 -// etc... -// -// Adjusting with rounding bias is relatively trivial: -// -// double value = bias_ + exponential_distribution(mean)(); -// double rounded_value = std::round(value); -// bias_ = value - rounded_value; -// return rounded_value; -// -// This class is thread-compatible. -class ExponentialBiased { - public: - // The number of bits set by NextRandom. - static constexpr int kPrngNumBits = 48; - - // `GetSkipCount()` returns the number of events to skip before some chosen - // event happens. For example, randomly tossing a coin, we will on average - // throw heads once before we get tails. We can simulate random coin tosses - // using GetSkipCount() as: - // - // ExponentialBiased eb; - // for (...) { - // int number_of_heads_before_tail = eb.GetSkipCount(1); - // for (int flips = 0; flips < number_of_heads_before_tail; ++flips) { - // printf("head..."); - // } - // printf("tail\n"); - // } - // - int64_t GetSkipCount(int64_t mean); - - // GetStride() returns the number of events required for a specific event to - // happen. See the class comments for a usage example. `GetStride()` is - // equivalent to `GetSkipCount(mean - 1) + 1`. When to use `GetStride()` or - // `GetSkipCount()` depends mostly on what best fits the use case. - int64_t GetStride(int64_t mean); - - // Computes a random number in the range [0, 1<<(kPrngNumBits+1) - 1] - // - // This is public to enable testing. - static uint64_t NextRandom(uint64_t rnd); - - private: - void Initialize(); - - uint64_t rng_{0}; - double bias_{0}; - bool initialized_{false}; -}; - -// Returns the next prng value. -// pRNG is: aX+b mod c with a = 0x5DEECE66D, b = 0xB, c = 1<<48 -// This is the lrand64 generator. -inline uint64_t ExponentialBiased::NextRandom(uint64_t rnd) { - const uint64_t prng_mult = uint64_t{0x5DEECE66D}; - const uint64_t prng_add = 0xB; - const uint64_t prng_mod_power = 48; - const uint64_t prng_mod_mask = - ~((~static_cast(0)) << prng_mod_power); - return (prng_mult * rnd + prng_add) & prng_mod_mask; -} - -} // namespace base_internal -ABSL_NAMESPACE_END -} // namespace absl - -#endif // ABSL_BASE_INTERNAL_EXPONENTIAL_BIASED_H_ diff --git a/Siv3D/include/ThirdParty/absl/base/internal/fast_type_id.h b/Siv3D/include/ThirdParty/absl/base/internal/fast_type_id.h index 3db59e8374..a547b3a8bc 100644 --- a/Siv3D/include/ThirdParty/absl/base/internal/fast_type_id.h +++ b/Siv3D/include/ThirdParty/absl/base/internal/fast_type_id.h @@ -28,8 +28,10 @@ struct FastTypeTag { constexpr static char dummy_var = 0; }; +#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL template constexpr char FastTypeTag::dummy_var; +#endif // FastTypeId() evaluates at compile/link-time to a unique pointer for the // passed-in type. These are meant to be good match for keys into maps or diff --git a/Siv3D/include/ThirdParty/absl/base/internal/inline_variable.h b/Siv3D/include/ThirdParty/absl/base/internal/inline_variable.h index 130d8c2476..df933faff5 100644 --- a/Siv3D/include/ThirdParty/absl/base/internal/inline_variable.h +++ b/Siv3D/include/ThirdParty/absl/base/internal/inline_variable.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef ABSL_BASE_INTERNAL_INLINE_VARIABLE_EMULATION_H_ -#define ABSL_BASE_INTERNAL_INLINE_VARIABLE_EMULATION_H_ +#ifndef ABSL_BASE_INTERNAL_INLINE_VARIABLE_H_ +#define ABSL_BASE_INTERNAL_INLINE_VARIABLE_H_ #include @@ -104,4 +104,4 @@ #endif // __cpp_inline_variables -#endif // ABSL_BASE_INTERNAL_INLINE_VARIABLE_EMULATION_H_ +#endif // ABSL_BASE_INTERNAL_INLINE_VARIABLE_H_ diff --git a/Siv3D/include/ThirdParty/absl/base/internal/inline_variable_testing.h b/Siv3D/include/ThirdParty/absl/base/internal/inline_variable_testing.h index 3856b9f80f..f3c81459fa 100644 --- a/Siv3D/include/ThirdParty/absl/base/internal/inline_variable_testing.h +++ b/Siv3D/include/ThirdParty/absl/base/internal/inline_variable_testing.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef ABSL_BASE_INLINE_VARIABLE_TESTING_H_ -#define ABSL_BASE_INLINE_VARIABLE_TESTING_H_ +#ifndef ABSL_BASE_INTERNAL_INLINE_VARIABLE_TESTING_H_ +#define ABSL_BASE_INTERNAL_INLINE_VARIABLE_TESTING_H_ #include "absl/base/internal/inline_variable.h" @@ -43,4 +43,4 @@ const int& get_int_b(); ABSL_NAMESPACE_END } // namespace absl -#endif // ABSL_BASE_INLINE_VARIABLE_TESTING_H_ +#endif // ABSL_BASE_INTERNAL_INLINE_VARIABLE_TESTING_H_ diff --git a/Siv3D/include/ThirdParty/absl/base/internal/invoke.h b/Siv3D/include/ThirdParty/absl/base/internal/invoke.h index 5c71f32823..643c2a42f0 100644 --- a/Siv3D/include/ThirdParty/absl/base/internal/invoke.h +++ b/Siv3D/include/ThirdParty/absl/base/internal/invoke.h @@ -14,6 +14,8 @@ // // absl::base_internal::invoke(f, args...) is an implementation of // INVOKE(f, args...) from section [func.require] of the C++ standard. +// When compiled as C++17 and later versions, it is implemented as an alias of +// std::invoke. // // [func.require] // Define INVOKE (f, t1, t2, ..., tN) as follows: @@ -35,6 +37,26 @@ #ifndef ABSL_BASE_INTERNAL_INVOKE_H_ #define ABSL_BASE_INTERNAL_INVOKE_H_ +#include "absl/base/config.h" + +#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L + +#include + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +using std::invoke; +using std::invoke_result_t; +using std::is_invocable_r; + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#else // ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L + #include #include #include @@ -80,8 +102,18 @@ struct MemFunAndRef : StrippedAccept { static decltype((std::declval().* std::declval())(std::declval()...)) Invoke(MemFun&& mem_fun, Obj&& obj, Args&&... args) { +// Ignore bogus GCC warnings on this line. +// See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101436 for similar example. +#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(11, 0) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Warray-bounds" +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif return (std::forward(obj).* std::forward(mem_fun))(std::forward(args)...); +#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(11, 0) +#pragma GCC diagnostic pop +#endif } }; @@ -180,8 +212,30 @@ invoke_result_t invoke(F&& f, Args&&... args) { return Invoker::type::Invoke(std::forward(f), std::forward(args)...); } + +template +struct IsInvocableRImpl : std::false_type {}; + +template +struct IsInvocableRImpl< + absl::void_t >, R, F, + Args...> + : std::integral_constant< + bool, + std::is_convertible, + R>::value || + std::is_void::value> {}; + +// Type trait whose member `value` is true if invoking `F` with `Args` is valid, +// and either the return type is convertible to `R`, or `R` is void. +// C++11-compatible version of `std::is_invocable_r`. +template +using is_invocable_r = IsInvocableRImpl; + } // namespace base_internal ABSL_NAMESPACE_END } // namespace absl +#endif // ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L + #endif // ABSL_BASE_INTERNAL_INVOKE_H_ diff --git a/Siv3D/include/ThirdParty/absl/base/internal/low_level_alloc.h b/Siv3D/include/ThirdParty/absl/base/internal/low_level_alloc.h index db91951c82..c2f1f25d8e 100644 --- a/Siv3D/include/ThirdParty/absl/base/internal/low_level_alloc.h +++ b/Siv3D/include/ThirdParty/absl/base/internal/low_level_alloc.h @@ -46,7 +46,8 @@ // for more information. #ifdef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING #error ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING cannot be directly set -#elif defined(_WIN32) || defined(__asmjs__) || defined(__wasm__) +#elif defined(_WIN32) || defined(__asmjs__) || defined(__wasm__) || \ + defined(__hexagon__) #define ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING 1 #endif @@ -103,7 +104,7 @@ class LowLevelAlloc { // the provided flags. For example, the call NewArena(kAsyncSignalSafe) // is itself async-signal-safe, as well as generatating an arena that provides // async-signal-safe Alloc/Free. - static Arena *NewArena(int32_t flags); + static Arena *NewArena(uint32_t flags); // Destroys an arena allocated by NewArena and returns true, // provided no allocated blocks remain in the arena. diff --git a/Siv3D/include/ThirdParty/absl/base/internal/low_level_scheduling.h b/Siv3D/include/ThirdParty/absl/base/internal/low_level_scheduling.h index 6ef79fbf90..9baccc0659 100644 --- a/Siv3D/include/ThirdParty/absl/base/internal/low_level_scheduling.h +++ b/Siv3D/include/ThirdParty/absl/base/internal/low_level_scheduling.h @@ -61,6 +61,8 @@ class SchedulingGuard { public: // Returns true iff the calling thread may be cooperatively rescheduled. static bool ReschedulingIsAllowed(); + SchedulingGuard(const SchedulingGuard&) = delete; + SchedulingGuard& operator=(const SchedulingGuard&) = delete; private: // Disable cooperative rescheduling of the calling thread. It may still @@ -101,9 +103,6 @@ class SchedulingGuard { friend class SchedulingHelper; friend class SpinLock; friend int absl::synchronization_internal::MutexDelay(int32_t c, int mode); - - SchedulingGuard(const SchedulingGuard&) = delete; - SchedulingGuard& operator=(const SchedulingGuard&) = delete; }; //------------------------------------------------------------------------------ diff --git a/Siv3D/include/ThirdParty/absl/base/internal/nullability_impl.h b/Siv3D/include/ThirdParty/absl/base/internal/nullability_impl.h new file mode 100644 index 0000000000..36e1b33d65 --- /dev/null +++ b/Siv3D/include/ThirdParty/absl/base/internal/nullability_impl.h @@ -0,0 +1,106 @@ +// Copyright 2023 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_BASE_INTERNAL_NULLABILITY_IMPL_H_ +#define ABSL_BASE_INTERNAL_NULLABILITY_IMPL_H_ + +#include +#include + +#include "absl/base/attributes.h" +#include "absl/meta/type_traits.h" + +namespace absl { + +namespace nullability_internal { + +// `IsNullabilityCompatible` checks whether its first argument is a class +// explicitly tagged as supporting nullability annotations. The tag is the type +// declaration `absl_nullability_compatible`. +template +struct IsNullabilityCompatible : std::false_type {}; + +template +struct IsNullabilityCompatible< + T, absl::void_t> : std::true_type { +}; + +template +constexpr bool IsSupportedType = IsNullabilityCompatible::value; + +template +constexpr bool IsSupportedType = true; + +template +constexpr bool IsSupportedType = true; + +template +constexpr bool IsSupportedType> = true; + +template +constexpr bool IsSupportedType> = true; + +template +struct EnableNullable { + static_assert(nullability_internal::IsSupportedType>, + "Template argument must be a raw or supported smart pointer " + "type. See absl/base/nullability.h."); + using type = T; +}; + +template +struct EnableNonnull { + static_assert(nullability_internal::IsSupportedType>, + "Template argument must be a raw or supported smart pointer " + "type. See absl/base/nullability.h."); + using type = T; +}; + +template +struct EnableNullabilityUnknown { + static_assert(nullability_internal::IsSupportedType>, + "Template argument must be a raw or supported smart pointer " + "type. See absl/base/nullability.h."); + using type = T; +}; + +// Note: we do not apply Clang nullability attributes (e.g. _Nullable). These +// only support raw pointers, and conditionally enabling them only for raw +// pointers inhibits template arg deduction. Ideally, they would support all +// pointer-like types. +template ::type> +using NullableImpl +#if ABSL_HAVE_CPP_ATTRIBUTE(clang::annotate) + [[clang::annotate("Nullable")]] +#endif + = T; + +template ::type> +using NonnullImpl +#if ABSL_HAVE_CPP_ATTRIBUTE(clang::annotate) + [[clang::annotate("Nonnull")]] +#endif + = T; + +template ::type> +using NullabilityUnknownImpl +#if ABSL_HAVE_CPP_ATTRIBUTE(clang::annotate) + [[clang::annotate("Nullability_Unspecified")]] +#endif + = T; + +} // namespace nullability_internal +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_NULLABILITY_IMPL_H_ diff --git a/Siv3D/include/ThirdParty/absl/base/internal/periodic_sampler.h b/Siv3D/include/ThirdParty/absl/base/internal/periodic_sampler.h deleted file mode 100644 index f8a86796b1..0000000000 --- a/Siv3D/include/ThirdParty/absl/base/internal/periodic_sampler.h +++ /dev/null @@ -1,211 +0,0 @@ -// Copyright 2019 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef ABSL_BASE_INTERNAL_PERIODIC_SAMPLER_H_ -#define ABSL_BASE_INTERNAL_PERIODIC_SAMPLER_H_ - -#include - -#include - -#include "absl/base/internal/exponential_biased.h" -#include "absl/base/optimization.h" - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace base_internal { - -// PeriodicSamplerBase provides the basic period sampler implementation. -// -// This is the base class for the templated PeriodicSampler class, which holds -// a global std::atomic value identified by a user defined tag, such that -// each specific PeriodSampler implementation holds its own global period. -// -// PeriodicSamplerBase is thread-compatible except where stated otherwise. -class PeriodicSamplerBase { - public: - // PeriodicSamplerBase is trivial / copyable / movable / destructible. - PeriodicSamplerBase() = default; - PeriodicSamplerBase(PeriodicSamplerBase&&) = default; - PeriodicSamplerBase(const PeriodicSamplerBase&) = default; - - // Returns true roughly once every `period` calls. This is established by a - // randomly picked `stride` that is counted down on each call to `Sample`. - // This stride is picked such that the probability of `Sample()` returning - // true is 1 in `period`. - inline bool Sample() noexcept; - - // The below methods are intended for optimized use cases where the - // size of the inlined fast path code is highly important. Applications - // should use the `Sample()` method unless they have proof that their - // specific use case requires the optimizations offered by these methods. - // - // An example of such a use case is SwissTable sampling. All sampling checks - // are in inlined SwissTable methods, and the number of call sites is huge. - // In this case, the inlined code size added to each translation unit calling - // SwissTable methods is non-trivial. - // - // The `SubtleMaybeSample()` function spuriously returns true even if the - // function should not be sampled, applications MUST match each call to - // 'SubtleMaybeSample()' returning true with a `SubtleConfirmSample()` call, - // and use the result of the latter as the sampling decision. - // In other words: the code should logically be equivalent to: - // - // if (SubtleMaybeSample() && SubtleConfirmSample()) { - // // Sample this call - // } - // - // In the 'inline-size' optimized case, the `SubtleConfirmSample()` call can - // be placed out of line, for example, the typical use case looks as follows: - // - // // --- frobber.h ----------- - // void FrobberSampled(); - // - // inline void FrobberImpl() { - // // ... - // } - // - // inline void Frobber() { - // if (ABSL_PREDICT_FALSE(sampler.SubtleMaybeSample())) { - // FrobberSampled(); - // } else { - // FrobberImpl(); - // } - // } - // - // // --- frobber.cc ----------- - // void FrobberSampled() { - // if (!sampler.SubtleConfirmSample())) { - // // Spurious false positive - // FrobberImpl(); - // return; - // } - // - // // Sampled execution - // // ... - // } - inline bool SubtleMaybeSample() noexcept; - bool SubtleConfirmSample() noexcept; - - protected: - // We explicitly don't use a virtual destructor as this class is never - // virtually destroyed, and it keeps the class trivial, which avoids TLS - // prologue and epilogue code for our TLS instances. - ~PeriodicSamplerBase() = default; - - // Returns the next stride for our sampler. - // This function is virtual for testing purposes only. - virtual int64_t GetExponentialBiased(int period) noexcept; - - private: - // Returns the current period of this sampler. Thread-safe. - virtual int period() const noexcept = 0; - - // Keep and decrement stride_ as an unsigned integer, but compare the value - // to zero casted as a signed int. clang and msvc do not create optimum code - // if we use signed for the combined decrement and sign comparison. - // - // Below 3 alternative options, all compiles generate the best code - // using the unsigned increment <---> signed int comparison option. - // - // Option 1: - // int64_t stride_; - // if (ABSL_PREDICT_TRUE(++stride_ < 0)) { ... } - // - // GCC x64 (OK) : https://gcc.godbolt.org/z/R5MzzA - // GCC ppc (OK) : https://gcc.godbolt.org/z/z7NZAt - // Clang x64 (BAD): https://gcc.godbolt.org/z/t4gPsd - // ICC x64 (OK) : https://gcc.godbolt.org/z/rE6s8W - // MSVC x64 (OK) : https://gcc.godbolt.org/z/ARMXqS - // - // Option 2: - // int64_t stride_ = 0; - // if (ABSL_PREDICT_TRUE(--stride_ >= 0)) { ... } - // - // GCC x64 (OK) : https://gcc.godbolt.org/z/jSQxYK - // GCC ppc (OK) : https://gcc.godbolt.org/z/VJdYaA - // Clang x64 (BAD): https://gcc.godbolt.org/z/Xm4NjX - // ICC x64 (OK) : https://gcc.godbolt.org/z/4snaFd - // MSVC x64 (BAD): https://gcc.godbolt.org/z/BgnEKE - // - // Option 3: - // uint64_t stride_; - // if (ABSL_PREDICT_TRUE(static_cast(++stride_) < 0)) { ... } - // - // GCC x64 (OK) : https://gcc.godbolt.org/z/bFbfPy - // GCC ppc (OK) : https://gcc.godbolt.org/z/S9KkUE - // Clang x64 (OK) : https://gcc.godbolt.org/z/UYzRb4 - // ICC x64 (OK) : https://gcc.godbolt.org/z/ptTNfD - // MSVC x64 (OK) : https://gcc.godbolt.org/z/76j4-5 - uint64_t stride_ = 0; - ExponentialBiased rng_; -}; - -inline bool PeriodicSamplerBase::SubtleMaybeSample() noexcept { - // See comments on `stride_` for the unsigned increment / signed compare. - if (ABSL_PREDICT_TRUE(static_cast(++stride_) < 0)) { - return false; - } - return true; -} - -inline bool PeriodicSamplerBase::Sample() noexcept { - return ABSL_PREDICT_FALSE(SubtleMaybeSample()) ? SubtleConfirmSample() - : false; -} - -// PeriodicSampler is a concreted periodic sampler implementation. -// The user provided Tag identifies the implementation, and is required to -// isolate the global state of this instance from other instances. -// -// Typical use case: -// -// struct HashTablezTag {}; -// thread_local PeriodicSampler sampler; -// -// void HashTableSamplingLogic(...) { -// if (sampler.Sample()) { -// HashTableSlowSamplePath(...); -// } -// } -// -template -class PeriodicSampler final : public PeriodicSamplerBase { - public: - ~PeriodicSampler() = default; - - int period() const noexcept final { - return period_.load(std::memory_order_relaxed); - } - - // Sets the global period for this sampler. Thread-safe. - // Setting a period of 0 disables the sampler, i.e., every call to Sample() - // will return false. Setting a period of 1 puts the sampler in 'always on' - // mode, i.e., every call to Sample() returns true. - static void SetGlobalPeriod(int period) { - period_.store(period, std::memory_order_relaxed); - } - - private: - static std::atomic period_; -}; - -template -std::atomic PeriodicSampler::period_(default_period); - -} // namespace base_internal -ABSL_NAMESPACE_END -} // namespace absl - -#endif // ABSL_BASE_INTERNAL_PERIODIC_SAMPLER_H_ diff --git a/Siv3D/include/ThirdParty/absl/base/internal/prefetch.h b/Siv3D/include/ThirdParty/absl/base/internal/prefetch.h new file mode 100644 index 0000000000..aecfd87781 --- /dev/null +++ b/Siv3D/include/ThirdParty/absl/base/internal/prefetch.h @@ -0,0 +1,137 @@ +// Copyright 2022 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// TODO(b/265984188): remove all uses and delete this header. + +#ifndef ABSL_BASE_INTERNAL_PREFETCH_H_ +#define ABSL_BASE_INTERNAL_PREFETCH_H_ + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/base/prefetch.h" + +#ifdef __SSE__ +#include +#endif + +#if defined(_MSC_VER) && defined(ABSL_INTERNAL_HAVE_SSE) +#include +#pragma intrinsic(_mm_prefetch) +#endif + +// Compatibility wrappers around __builtin_prefetch, to prefetch data +// for read if supported by the toolchain. + +// Move data into the cache before it is read, or "prefetch" it. +// +// The value of `addr` is the address of the memory to prefetch. If +// the target and compiler support it, data prefetch instructions are +// generated. If the prefetch is done some time before the memory is +// read, it may be in the cache by the time the read occurs. +// +// The function names specify the temporal locality heuristic applied, +// using the names of Intel prefetch instructions: +// +// T0 - high degree of temporal locality; data should be left in as +// many levels of the cache possible +// T1 - moderate degree of temporal locality +// T2 - low degree of temporal locality +// Nta - no temporal locality, data need not be left in the cache +// after the read +// +// Incorrect or gratuitous use of these functions can degrade +// performance, so use them only when representative benchmarks show +// an improvement. +// +// Example usage: +// +// absl::base_internal::PrefetchT0(addr); +// +// Currently, the different prefetch calls behave on some Intel +// architectures as follows: +// +// SNB..SKL SKX +// PrefetchT0() L1/L2/L3 L1/L2 +// PrefetchT1() L2/L3 L2 +// PrefetchT2() L2/L3 L2 +// PrefetchNta() L1/--/L3 L1* +// +// * On SKX PrefetchNta() will bring the line into L1 but will evict +// from L3 cache. This might result in surprising behavior. +// +// SNB = Sandy Bridge, SKL = Skylake, SKX = Skylake Xeon. +// +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +ABSL_DEPRECATED("Use absl::PrefetchToLocalCache() instead") +inline void PrefetchT0(const void* address) { + absl::PrefetchToLocalCache(address); +} + +ABSL_DEPRECATED("Use absl::PrefetchToLocalCache() instead") +inline void PrefetchNta(const void* address) { + absl::PrefetchToLocalCacheNta(address); +} + +ABSL_DEPRECATED("Use __builtin_prefetch() for advanced prefetch logic instead") +void PrefetchT1(const void* addr); + +ABSL_DEPRECATED("Use __builtin_prefetch() for advanced prefetch logic instead") +void PrefetchT2(const void* addr); + +// Implementation details follow. + +#if ABSL_HAVE_BUILTIN(__builtin_prefetch) || defined(__GNUC__) + +#define ABSL_INTERNAL_HAVE_PREFETCH 1 + +// See __builtin_prefetch: +// https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html. +// +// These functions speculatively load for read only. This is +// safe for all currently supported platforms. However, prefetch for +// store may have problems depending on the target platform. +// +inline void PrefetchT1(const void* addr) { + // Note: this uses prefetcht1 on Intel. + __builtin_prefetch(addr, 0, 2); +} +inline void PrefetchT2(const void* addr) { + // Note: this uses prefetcht2 on Intel. + __builtin_prefetch(addr, 0, 1); +} + +#elif defined(ABSL_INTERNAL_HAVE_SSE) + +#define ABSL_INTERNAL_HAVE_PREFETCH 1 + +inline void PrefetchT1(const void* addr) { + _mm_prefetch(reinterpret_cast(addr), _MM_HINT_T1); +} +inline void PrefetchT2(const void* addr) { + _mm_prefetch(reinterpret_cast(addr), _MM_HINT_T2); +} + +#else +inline void PrefetchT1(const void*) {} +inline void PrefetchT2(const void*) {} +#endif + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_PREFETCH_H_ diff --git a/Siv3D/include/ThirdParty/absl/base/internal/raw_logging.h b/Siv3D/include/ThirdParty/absl/base/internal/raw_logging.h index 2508f3cfdb..b79550b2b7 100644 --- a/Siv3D/include/ThirdParty/absl/base/internal/raw_logging.h +++ b/Siv3D/include/ThirdParty/absl/base/internal/raw_logging.h @@ -43,12 +43,12 @@ #define ABSL_RAW_LOG(severity, ...) \ do { \ - constexpr const char* absl_raw_logging_internal_basename = \ - ::absl::raw_logging_internal::Basename(__FILE__, \ - sizeof(__FILE__) - 1); \ - ::absl::raw_logging_internal::RawLog(ABSL_RAW_LOGGING_INTERNAL_##severity, \ - absl_raw_logging_internal_basename, \ - __LINE__, __VA_ARGS__); \ + constexpr const char* absl_raw_log_internal_basename = \ + ::absl::raw_log_internal::Basename(__FILE__, sizeof(__FILE__) - 1); \ + ::absl::raw_log_internal::RawLog(ABSL_RAW_LOG_INTERNAL_##severity, \ + absl_raw_log_internal_basename, __LINE__, \ + __VA_ARGS__); \ + ABSL_RAW_LOG_INTERNAL_MAYBE_UNREACHABLE_##severity; \ } while (0) // Similar to CHECK(condition) << message, but for low-level modules: @@ -72,12 +72,13 @@ // // The API is a subset of the above: each macro only takes two arguments. Use // StrCat if you need to build a richer message. -#define ABSL_INTERNAL_LOG(severity, message) \ - do { \ - constexpr const char* absl_raw_logging_internal_filename = __FILE__; \ - ::absl::raw_logging_internal::internal_log_function( \ - ABSL_RAW_LOGGING_INTERNAL_##severity, \ - absl_raw_logging_internal_filename, __LINE__, message); \ +#define ABSL_INTERNAL_LOG(severity, message) \ + do { \ + constexpr const char* absl_raw_log_internal_filename = __FILE__; \ + ::absl::raw_log_internal::internal_log_function( \ + ABSL_RAW_LOG_INTERNAL_##severity, absl_raw_log_internal_filename, \ + __LINE__, message); \ + ABSL_RAW_LOG_INTERNAL_MAYBE_UNREACHABLE_##severity; \ } while (0) #define ABSL_INTERNAL_CHECK(condition, message) \ @@ -89,16 +90,36 @@ } \ } while (0) -#define ABSL_RAW_LOGGING_INTERNAL_INFO ::absl::LogSeverity::kInfo -#define ABSL_RAW_LOGGING_INTERNAL_WARNING ::absl::LogSeverity::kWarning -#define ABSL_RAW_LOGGING_INTERNAL_ERROR ::absl::LogSeverity::kError -#define ABSL_RAW_LOGGING_INTERNAL_FATAL ::absl::LogSeverity::kFatal -#define ABSL_RAW_LOGGING_INTERNAL_LEVEL(severity) \ +#ifndef NDEBUG + +#define ABSL_RAW_DLOG(severity, ...) ABSL_RAW_LOG(severity, __VA_ARGS__) +#define ABSL_RAW_DCHECK(condition, message) ABSL_RAW_CHECK(condition, message) + +#else // NDEBUG + +#define ABSL_RAW_DLOG(severity, ...) \ + while (false) ABSL_RAW_LOG(severity, __VA_ARGS__) +#define ABSL_RAW_DCHECK(condition, message) \ + while (false) ABSL_RAW_CHECK(condition, message) + +#endif // NDEBUG + +#define ABSL_RAW_LOG_INTERNAL_INFO ::absl::LogSeverity::kInfo +#define ABSL_RAW_LOG_INTERNAL_WARNING ::absl::LogSeverity::kWarning +#define ABSL_RAW_LOG_INTERNAL_ERROR ::absl::LogSeverity::kError +#define ABSL_RAW_LOG_INTERNAL_FATAL ::absl::LogSeverity::kFatal +#define ABSL_RAW_LOG_INTERNAL_LEVEL(severity) \ ::absl::NormalizeLogSeverity(severity) +#define ABSL_RAW_LOG_INTERNAL_MAYBE_UNREACHABLE_INFO +#define ABSL_RAW_LOG_INTERNAL_MAYBE_UNREACHABLE_WARNING +#define ABSL_RAW_LOG_INTERNAL_MAYBE_UNREACHABLE_ERROR +#define ABSL_RAW_LOG_INTERNAL_MAYBE_UNREACHABLE_FATAL ABSL_UNREACHABLE() +#define ABSL_RAW_LOG_INTERNAL_MAYBE_UNREACHABLE_LEVEL(severity) + namespace absl { ABSL_NAMESPACE_BEGIN -namespace raw_logging_internal { +namespace raw_log_internal { // Helper function to implement ABSL_RAW_LOG // Logs format... at "severity" level, reporting it @@ -107,12 +128,9 @@ namespace raw_logging_internal { void RawLog(absl::LogSeverity severity, const char* file, int line, const char* format, ...) ABSL_PRINTF_ATTRIBUTE(4, 5); -// Writes the provided buffer directly to stderr, in a safe, low-level manner. -// -// In POSIX this means calling write(), which is async-signal safe and does -// not malloc. If the platform supports the SYS_write syscall, we invoke that -// directly to side-step any libc interception. -void SafeWriteToStderr(const char *s, size_t len); +// Writes the provided buffer directly to stderr, in a signal-safe, low-level +// manner. Preserves errno. +void AsyncSignalSafeWriteError(const char* s, size_t len); // compile-time function to get the "base" filename, that is, the part of // a filename after the last "/" or "\" path separator. The search starts at @@ -131,7 +149,7 @@ constexpr const char* Basename(const char* fname, int offset) { // TODO(gfalcon): Come up with a better name for this method. bool RawLoggingFullySupported(); -// Function type for a raw_logging customization hook for suppressing messages +// Function type for a raw_log customization hook for suppressing messages // by severity, and for writing custom prefixes on non-suppressed messages. // // The installed hook is called for every raw log invocation. The message will @@ -140,19 +158,20 @@ bool RawLoggingFullySupported(); // also provided with an output buffer, where it can write a custom log message // prefix. // -// The raw_logging system does not allocate memory or grab locks. User-provided +// The raw_log system does not allocate memory or grab locks. User-provided // hooks must avoid these operations, and must not throw exceptions. // // 'severity' is the severity level of the message being written. // 'file' and 'line' are the file and line number where the ABSL_RAW_LOG macro // was located. -// 'buffer' and 'buf_size' are pointers to the buffer and buffer size. If the -// hook writes a prefix, it must increment *buffer and decrement *buf_size +// 'buf' and 'buf_size' are pointers to the buffer and buffer size. If the +// hook writes a prefix, it must increment *buf and decrement *buf_size // accordingly. -using LogPrefixHook = bool (*)(absl::LogSeverity severity, const char* file, - int line, char** buffer, int* buf_size); +using LogFilterAndPrefixHook = bool (*)(absl::LogSeverity severity, + const char* file, int line, char** buf, + int* buf_size); -// Function type for a raw_logging customization hook called to abort a process +// Function type for a raw_log customization hook called to abort a process // when a FATAL message is logged. If the provided AbortHook() returns, the // logging system will call abort(). // @@ -160,7 +179,10 @@ using LogPrefixHook = bool (*)(absl::LogSeverity severity, const char* file, // was located. // The NUL-terminated logged message lives in the buffer between 'buf_start' // and 'buf_end'. 'prefix_end' points to the first non-prefix character of the -// buffer (as written by the LogPrefixHook.) +// buffer (as written by the LogFilterAndPrefixHook.) +// +// The lifetime of the filename and message buffers will not end while the +// process remains alive. using AbortHook = void (*)(const char* file, int line, const char* buf_start, const char* prefix_end, const char* buf_end); @@ -176,9 +198,17 @@ ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES ABSL_DLL extern base_internal::AtomicHook< InternalLogFunction> internal_log_function; +// Registers hooks of the above types. Only a single hook of each type may be +// registered. It is an error to call these functions multiple times with +// different input arguments. +// +// These functions are safe to call at any point during initialization; they do +// not block or malloc, and are async-signal safe. +void RegisterLogFilterAndPrefixHook(LogFilterAndPrefixHook func); +void RegisterAbortHook(AbortHook func); void RegisterInternalLogFunction(InternalLogFunction func); -} // namespace raw_logging_internal +} // namespace raw_log_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/Siv3D/include/ThirdParty/absl/base/internal/spinlock.h b/Siv3D/include/ThirdParty/absl/base/internal/spinlock.h index e6ac9e6400..09ba5824b1 100644 --- a/Siv3D/include/ThirdParty/absl/base/internal/spinlock.h +++ b/Siv3D/include/ThirdParty/absl/base/internal/spinlock.h @@ -15,25 +15,22 @@ // // Most users requiring mutual exclusion should use Mutex. -// SpinLock is provided for use in three situations: -// - for use in code that Mutex itself depends on -// - to get a faster fast-path release under low contention (without an -// atomic read-modify-write) In return, SpinLock has worse behaviour under -// contention, which is why Mutex is preferred in most situations. +// SpinLock is provided for use in two situations: +// - for use by Abseil internal code that Mutex itself depends on // - for async signal safety (see below) // SpinLock is async signal safe. If a spinlock is used within a signal // handler, all code that acquires the lock must ensure that the signal cannot // arrive while they are holding the lock. Typically, this is done by blocking // the signal. +// +// Threads waiting on a SpinLock may be woken in an arbitrary order. #ifndef ABSL_BASE_INTERNAL_SPINLOCK_H_ #define ABSL_BASE_INTERNAL_SPINLOCK_H_ -#include -#include - #include +#include #include "absl/base/attributes.h" #include "absl/base/const_init.h" @@ -42,8 +39,6 @@ #include "absl/base/internal/raw_logging.h" #include "absl/base/internal/scheduling_mode.h" #include "absl/base/internal/tsan_mutex_interface.h" -#include "absl/base/macros.h" -#include "absl/base/port.h" #include "absl/base/thread_annotations.h" namespace absl { @@ -121,6 +116,14 @@ class ABSL_LOCKABLE SpinLock { return (lockword_.load(std::memory_order_relaxed) & kSpinLockHeld) != 0; } + // Return immediately if this thread holds the SpinLock exclusively. + // Otherwise, report an error by crashing with a diagnostic. + inline void AssertHeld() const ABSL_ASSERT_EXCLUSIVE_LOCK() { + if (!IsHeld()) { + ABSL_RAW_LOG(FATAL, "thread should hold the lock on SpinLock"); + } + } + protected: // These should not be exported except for testing. @@ -130,7 +133,7 @@ class ABSL_LOCKABLE SpinLock { int64_t wait_end_time); // Extract number of wait cycles in a lock value. - static uint64_t DecodeWaitCycles(uint32_t lock_value); + static int64_t DecodeWaitCycles(uint32_t lock_value); // Provide access to protected method above. Use for testing only. friend struct SpinLockTest; @@ -140,8 +143,20 @@ class ABSL_LOCKABLE SpinLock { // // bit[0] encodes whether a lock is being held. // bit[1] encodes whether a lock uses cooperative scheduling. - // bit[2] encodes whether a lock disables scheduling. + // bit[2] encodes whether the current lock holder disabled scheduling when + // acquiring the lock. Only set when kSpinLockHeld is also set. // bit[3:31] encodes time a lock spent on waiting as a 29-bit unsigned int. + // This is set by the lock holder to indicate how long it waited on + // the lock before eventually acquiring it. The number of cycles is + // encoded as a 29-bit unsigned int, or in the case that the current + // holder did not wait but another waiter is queued, the LSB + // (kSpinLockSleeper) is set. The implementation does not explicitly + // track the number of queued waiters beyond this. It must always be + // assumed that waiters may exist if the current holder was required to + // queue. + // + // Invariant: if the lock is not held, the value is either 0 or + // kSpinLockCooperative. static constexpr uint32_t kSpinLockHeld = 1; static constexpr uint32_t kSpinLockCooperative = 2; static constexpr uint32_t kSpinLockDisabledScheduling = 4; diff --git a/Siv3D/include/ThirdParty/absl/base/internal/spinlock_akaros.inc b/Siv3D/include/ThirdParty/absl/base/internal/spinlock_akaros.inc index bc468940fc..7b0cada4f1 100644 --- a/Siv3D/include/ThirdParty/absl/base/internal/spinlock_akaros.inc +++ b/Siv3D/include/ThirdParty/absl/base/internal/spinlock_akaros.inc @@ -20,7 +20,7 @@ extern "C" { -ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockDelay( +ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockDelay)( std::atomic* /* lock_word */, uint32_t /* value */, int /* loop */, absl::base_internal::SchedulingMode /* mode */) { // In Akaros, one must take care not to call anything that could cause a @@ -29,7 +29,7 @@ ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockDelay( // arbitrary code. } -ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockWake( +ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockWake)( std::atomic* /* lock_word */, bool /* all */) {} } // extern "C" diff --git a/Siv3D/include/ThirdParty/absl/base/internal/spinlock_linux.inc b/Siv3D/include/ThirdParty/absl/base/internal/spinlock_linux.inc index e31c6ed477..fe8ba674f5 100644 --- a/Siv3D/include/ThirdParty/absl/base/internal/spinlock_linux.inc +++ b/Siv3D/include/ThirdParty/absl/base/internal/spinlock_linux.inc @@ -56,18 +56,15 @@ static_assert(sizeof(std::atomic) == sizeof(int), extern "C" { -ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockDelay( - std::atomic *w, uint32_t value, int loop, +ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockDelay)( + std::atomic *w, uint32_t value, int, absl::base_internal::SchedulingMode) { absl::base_internal::ErrnoSaver errno_saver; - struct timespec tm; - tm.tv_sec = 0; - tm.tv_nsec = absl::base_internal::SpinLockSuggestedDelayNS(loop); - syscall(SYS_futex, w, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, value, &tm); + syscall(SYS_futex, w, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, value, nullptr); } -ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockWake(std::atomic *w, - bool all) { +ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockWake)( + std::atomic *w, bool all) { syscall(SYS_futex, w, FUTEX_WAKE | FUTEX_PRIVATE_FLAG, all ? INT_MAX : 1, 0); } diff --git a/Siv3D/include/ThirdParty/absl/base/internal/spinlock_posix.inc b/Siv3D/include/ThirdParty/absl/base/internal/spinlock_posix.inc index fcd21b151b..4f6f887d99 100644 --- a/Siv3D/include/ThirdParty/absl/base/internal/spinlock_posix.inc +++ b/Siv3D/include/ThirdParty/absl/base/internal/spinlock_posix.inc @@ -25,7 +25,7 @@ extern "C" { -ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockDelay( +ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockDelay)( std::atomic* /* lock_word */, uint32_t /* value */, int loop, absl::base_internal::SchedulingMode /* mode */) { absl::base_internal::ErrnoSaver errno_saver; @@ -40,7 +40,7 @@ ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockDelay( } } -ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockWake( +ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockWake)( std::atomic* /* lock_word */, bool /* all */) {} } // extern "C" diff --git a/Siv3D/include/ThirdParty/absl/base/internal/spinlock_wait.h b/Siv3D/include/ThirdParty/absl/base/internal/spinlock_wait.h index 169bc749fb..9a1adcda5e 100644 --- a/Siv3D/include/ThirdParty/absl/base/internal/spinlock_wait.h +++ b/Siv3D/include/ThirdParty/absl/base/internal/spinlock_wait.h @@ -39,22 +39,22 @@ struct SpinLockWaitTransition { // satisfying 0<=i *w, int n, const SpinLockWaitTransition trans[], SchedulingMode scheduling_mode); -// If possible, wake some thread that has called SpinLockDelay(w, ...). If -// "all" is true, wake all such threads. This call is a hint, and on some -// systems it may be a no-op; threads calling SpinLockDelay() will always wake -// eventually even if SpinLockWake() is never called. +// If possible, wake some thread that has called SpinLockDelay(w, ...). If `all` +// is true, wake all such threads. On some systems, this may be a no-op; on +// those systems, threads calling SpinLockDelay() will always wake eventually +// even if SpinLockWake() is never called. void SpinLockWake(std::atomic *w, bool all); // Wait for an appropriate spin delay on iteration "loop" of a // spin loop on location *w, whose previously observed value was "value". // SpinLockDelay() may do nothing, may yield the CPU, may sleep a clock tick, -// or may wait for a delay that can be truncated by a call to SpinLockWake(w). -// In all cases, it must return in bounded time even if SpinLockWake() is not -// called. +// or may wait for a call to SpinLockWake(w). void SpinLockDelay(std::atomic *w, uint32_t value, int loop, base_internal::SchedulingMode scheduling_mode); @@ -73,21 +73,23 @@ ABSL_NAMESPACE_END // By changing our extension points to be extern "C", we dodge this // check. extern "C" { -void AbslInternalSpinLockWake(std::atomic *w, bool all); -void AbslInternalSpinLockDelay( +void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockWake)(std::atomic *w, + bool all); +void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockDelay)( std::atomic *w, uint32_t value, int loop, absl::base_internal::SchedulingMode scheduling_mode); } inline void absl::base_internal::SpinLockWake(std::atomic *w, bool all) { - AbslInternalSpinLockWake(w, all); + ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockWake)(w, all); } inline void absl::base_internal::SpinLockDelay( std::atomic *w, uint32_t value, int loop, absl::base_internal::SchedulingMode scheduling_mode) { - AbslInternalSpinLockDelay(w, value, loop, scheduling_mode); + ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockDelay) + (w, value, loop, scheduling_mode); } #endif // ABSL_BASE_INTERNAL_SPINLOCK_WAIT_H_ diff --git a/Siv3D/include/ThirdParty/absl/base/internal/spinlock_win32.inc b/Siv3D/include/ThirdParty/absl/base/internal/spinlock_win32.inc index 78654b5b59..934c2016fb 100644 --- a/Siv3D/include/ThirdParty/absl/base/internal/spinlock_win32.inc +++ b/Siv3D/include/ThirdParty/absl/base/internal/spinlock_win32.inc @@ -20,18 +20,21 @@ extern "C" { -void AbslInternalSpinLockDelay(std::atomic* /* lock_word */, - uint32_t /* value */, int loop, - absl::base_internal::SchedulingMode /* mode */) { +void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockDelay)( + std::atomic* /* lock_word */, uint32_t /* value */, int loop, + absl::base_internal::SchedulingMode /* mode */) { if (loop == 0) { } else if (loop == 1) { Sleep(0); } else { - Sleep(absl::base_internal::SpinLockSuggestedDelayNS(loop) / 1000000); + // SpinLockSuggestedDelayNS() always returns a positive integer, so this + // static_cast is safe. + Sleep(static_cast( + absl::base_internal::SpinLockSuggestedDelayNS(loop) / 1000000)); } } -void AbslInternalSpinLockWake(std::atomic* /* lock_word */, - bool /* all */) {} +void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockWake)( + std::atomic* /* lock_word */, bool /* all */) {} } // extern "C" diff --git a/Siv3D/include/ThirdParty/absl/base/internal/thread_annotations.h b/Siv3D/include/ThirdParty/absl/base/internal/thread_annotations.h index 4dab6a9c15..8c5c67e0df 100644 --- a/Siv3D/include/ThirdParty/absl/base/internal/thread_annotations.h +++ b/Siv3D/include/ThirdParty/absl/base/internal/thread_annotations.h @@ -38,6 +38,13 @@ #ifndef ABSL_BASE_INTERNAL_THREAD_ANNOTATIONS_H_ #define ABSL_BASE_INTERNAL_THREAD_ANNOTATIONS_H_ +// ABSL_LEGACY_THREAD_ANNOTATIONS is a *temporary* compatibility macro that can +// be defined on the compile command-line to restore the legacy spellings of the +// thread annotations macros/functions. The macros in this file are available +// under ABSL_ prefixed spellings in absl/base/thread_annotations.h. This macro +// and the legacy spellings will be removed in the future. +#ifdef ABSL_LEGACY_THREAD_ANNOTATIONS + #if defined(__clang__) #define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x)) #else @@ -268,4 +275,6 @@ inline T& ts_unchecked_read(T& v) NO_THREAD_SAFETY_ANALYSIS { } // namespace thread_safety_analysis +#endif // defined(ABSL_LEGACY_THREAD_ANNOTATIONS) + #endif // ABSL_BASE_INTERNAL_THREAD_ANNOTATIONS_H_ diff --git a/Siv3D/include/ThirdParty/absl/base/internal/thread_identity.h b/Siv3D/include/ThirdParty/absl/base/internal/thread_identity.h index ceb109b41c..b6e917ce80 100644 --- a/Siv3D/include/ThirdParty/absl/base/internal/thread_identity.h +++ b/Siv3D/include/ThirdParty/absl/base/internal/thread_identity.h @@ -32,6 +32,7 @@ #include "absl/base/config.h" #include "absl/base/internal/per_thread_tls.h" +#include "absl/base/optimization.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -61,38 +62,36 @@ struct PerThreadSynch { return reinterpret_cast(this); } - PerThreadSynch *next; // Circular waiter queue; initialized to 0. - PerThreadSynch *skip; // If non-zero, all entries in Mutex queue + PerThreadSynch* next; // Circular waiter queue; initialized to 0. + PerThreadSynch* skip; // If non-zero, all entries in Mutex queue // up to and including "skip" have same // condition as this, and will be woken later bool may_skip; // if false while on mutex queue, a mutex unlocker // is using this PerThreadSynch as a terminator. Its // skip field must not be filled in because the loop // might then skip over the terminator. - - // The wait parameters of the current wait. waitp is null if the - // thread is not waiting. Transitions from null to non-null must - // occur before the enqueue commit point (state = kQueued in - // Enqueue() and CondVarEnqueue()). Transitions from non-null to - // null must occur after the wait is finished (state = kAvailable in - // Mutex::Block() and CondVar::WaitCommon()). This field may be - // changed only by the thread that describes this PerThreadSynch. A - // special case is Fer(), which calls Enqueue() on another thread, - // but with an identical SynchWaitParams pointer, thus leaving the - // pointer unchanged. - SynchWaitParams *waitp; - - bool suppress_fatal_errors; // If true, try to proceed even in the face of - // broken invariants. This is used within fatal - // signal handlers to improve the chances of - // debug logging information being output - // successfully. - - intptr_t readers; // Number of readers in mutex. - int priority; // Priority of thread (updated every so often). - - // When priority will next be read (cycles). - int64_t next_priority_read_cycles; + bool wake; // This thread is to be woken from a Mutex. + // If "x" is on a waiter list for a mutex, "x->cond_waiter" is true iff the + // waiter is waiting on the mutex as part of a CV Wait or Mutex Await. + // + // The value of "x->cond_waiter" is meaningless if "x" is not on a + // Mutex waiter list. + bool cond_waiter; + bool maybe_unlocking; // Valid at head of Mutex waiter queue; + // true if UnlockSlow could be searching + // for a waiter to wake. Used for an optimization + // in Enqueue(). true is always a valid value. + // Can be reset to false when the unlocker or any + // writer releases the lock, or a reader fully + // releases the lock. It may not be set to false + // by a reader that decrements the count to + // non-zero. protected by mutex spinlock + bool suppress_fatal_errors; // If true, try to proceed even in the face + // of broken invariants. This is used within + // fatal signal handlers to improve the + // chances of debug logging information being + // output successfully. + int priority; // Priority of thread (updated every so often). // State values: // kAvailable: This PerThreadSynch is available. @@ -105,36 +104,37 @@ struct PerThreadSynch { // // Transitions from kAvailable to kQueued require no barrier, they // are externally ordered by the Mutex. - enum State { - kAvailable, - kQueued - }; + enum State { kAvailable, kQueued }; std::atomic state; - bool maybe_unlocking; // Valid at head of Mutex waiter queue; - // true if UnlockSlow could be searching - // for a waiter to wake. Used for an optimization - // in Enqueue(). true is always a valid value. - // Can be reset to false when the unlocker or any - // writer releases the lock, or a reader fully releases - // the lock. It may not be set to false by a reader - // that decrements the count to non-zero. - // protected by mutex spinlock + // The wait parameters of the current wait. waitp is null if the + // thread is not waiting. Transitions from null to non-null must + // occur before the enqueue commit point (state = kQueued in + // Enqueue() and CondVarEnqueue()). Transitions from non-null to + // null must occur after the wait is finished (state = kAvailable in + // Mutex::Block() and CondVar::WaitCommon()). This field may be + // changed only by the thread that describes this PerThreadSynch. A + // special case is Fer(), which calls Enqueue() on another thread, + // but with an identical SynchWaitParams pointer, thus leaving the + // pointer unchanged. + SynchWaitParams* waitp; - bool wake; // This thread is to be woken from a Mutex. + intptr_t readers; // Number of readers in mutex. - // If "x" is on a waiter list for a mutex, "x->cond_waiter" is true iff the - // waiter is waiting on the mutex as part of a CV Wait or Mutex Await. - // - // The value of "x->cond_waiter" is meaningless if "x" is not on a - // Mutex waiter list. - bool cond_waiter; + // When priority will next be read (cycles). + int64_t next_priority_read_cycles; // Locks held; used during deadlock detection. // Allocated in Synch_GetAllLocks() and freed in ReclaimThreadIdentity(). - SynchLocksHeld *all_locks; + SynchLocksHeld* all_locks; }; +// The instances of this class are allocated in NewThreadIdentity() with an +// alignment of PerThreadSynch::kAlignment. +// +// NOTE: The layout of fields in this structure is critical, please do not +// add, remove, or modify the field placements without fully auditing the +// layout. struct ThreadIdentity { // Must be the first member. The Mutex implementation requires that // the PerThreadSynch object associated with each thread is @@ -144,7 +144,7 @@ struct ThreadIdentity { // Private: Reserved for absl::synchronization_internal::Waiter. struct WaiterState { - char data[128]; + alignas(void*) char data[256]; } waiter_state; // Used by PerThreadSem::{Get,Set}ThreadBlockedCounter(). @@ -167,7 +167,10 @@ struct ThreadIdentity { // // Does not malloc(*), and is async-signal safe. // [*] Technically pthread_setspecific() does malloc on first use; however this -// is handled internally within tcmalloc's initialization already. +// is handled internally within tcmalloc's initialization already. Note that +// darwin does *not* use tcmalloc, so this can catch you if using MallocHooks +// on Apple platforms. Whatever function is calling your MallocHooks will need +// to watch for recursion on Apple platforms. // // New ThreadIdentity objects can be constructed and associated with a thread // by calling GetOrCreateCurrentThreadIdentity() in per-thread-sem.h. @@ -189,29 +192,31 @@ void ClearCurrentThreadIdentity(); // May be chosen at compile time via: -DABSL_FORCE_THREAD_IDENTITY_MODE= #ifdef ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC -#error ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC cannot be direcly set +#error ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC cannot be directly set #else #define ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC 0 #endif #ifdef ABSL_THREAD_IDENTITY_MODE_USE_TLS -#error ABSL_THREAD_IDENTITY_MODE_USE_TLS cannot be direcly set +#error ABSL_THREAD_IDENTITY_MODE_USE_TLS cannot be directly set #else #define ABSL_THREAD_IDENTITY_MODE_USE_TLS 1 #endif #ifdef ABSL_THREAD_IDENTITY_MODE_USE_CPP11 -#error ABSL_THREAD_IDENTITY_MODE_USE_CPP11 cannot be direcly set +#error ABSL_THREAD_IDENTITY_MODE_USE_CPP11 cannot be directly set #else #define ABSL_THREAD_IDENTITY_MODE_USE_CPP11 2 #endif #ifdef ABSL_THREAD_IDENTITY_MODE -#error ABSL_THREAD_IDENTITY_MODE cannot be direcly set +#error ABSL_THREAD_IDENTITY_MODE cannot be directly set #elif defined(ABSL_FORCE_THREAD_IDENTITY_MODE) #define ABSL_THREAD_IDENTITY_MODE ABSL_FORCE_THREAD_IDENTITY_MODE #elif defined(_WIN32) && !defined(__MINGW32__) #define ABSL_THREAD_IDENTITY_MODE ABSL_THREAD_IDENTITY_MODE_USE_CPP11 +#elif defined(__APPLE__) && defined(ABSL_HAVE_THREAD_LOCAL) +#define ABSL_THREAD_IDENTITY_MODE ABSL_THREAD_IDENTITY_MODE_USE_CPP11 #elif ABSL_PER_THREAD_TLS && defined(__GOOGLE_GRTE_VERSION__) && \ (__GOOGLE_GRTE_VERSION__ >= 20140228L) // Support for async-safe TLS was specifically added in GRTEv4. It's not @@ -235,13 +240,18 @@ ABSL_CONST_INIT extern thread_local ThreadIdentity* thread_identity_ptr; #error Thread-local storage not detected on this platform #endif -// thread_local variables cannot be in headers exposed by DLLs. However, it is -// important for performance reasons in general that -// `CurrentThreadIdentityIfPresent` be inlined. This is not possible across a -// DLL boundary so, with DLLs, we opt to have the function not be inlined. Note +// thread_local variables cannot be in headers exposed by DLLs or in certain +// build configurations on Apple platforms. However, it is important for +// performance reasons in general that `CurrentThreadIdentityIfPresent` be +// inlined. In the other cases we opt to have the function not be inlined. Note // that `CurrentThreadIdentityIfPresent` is declared above so we can exclude -// this entire inline definition when compiling as a DLL. -#if !defined(ABSL_BUILD_DLL) && !defined(ABSL_CONSUME_DLL) +// this entire inline definition. +#if !defined(__APPLE__) && !defined(ABSL_BUILD_DLL) && \ + !defined(ABSL_CONSUME_DLL) +#define ABSL_INTERNAL_INLINE_CURRENT_THREAD_IDENTITY_IF_PRESENT 1 +#endif + +#ifdef ABSL_INTERNAL_INLINE_CURRENT_THREAD_IDENTITY_IF_PRESENT inline ThreadIdentity* CurrentThreadIdentityIfPresent() { return thread_identity_ptr; } diff --git a/Siv3D/include/ThirdParty/absl/base/internal/unaligned_access.h b/Siv3D/include/ThirdParty/absl/base/internal/unaligned_access.h index dd5250de8e..093dd9b499 100644 --- a/Siv3D/include/ThirdParty/absl/base/internal/unaligned_access.h +++ b/Siv3D/include/ThirdParty/absl/base/internal/unaligned_access.h @@ -31,80 +31,6 @@ // The unaligned API is C++ only. The declarations use C++ features // (namespaces, inline) which are absent or incompatible in C. #if defined(__cplusplus) - -#if defined(ABSL_HAVE_ADDRESS_SANITIZER) || \ - defined(ABSL_HAVE_THREAD_SANITIZER) || defined(ABSL_HAVE_MEMORY_SANITIZER) -// Consider we have an unaligned load/store of 4 bytes from address 0x...05. -// AddressSanitizer will treat it as a 3-byte access to the range 05:07 and -// will miss a bug if 08 is the first unaddressable byte. -// ThreadSanitizer will also treat this as a 3-byte access to 05:07 and will -// miss a race between this access and some other accesses to 08. -// MemorySanitizer will correctly propagate the shadow on unaligned stores -// and correctly report bugs on unaligned loads, but it may not properly -// update and report the origin of the uninitialized memory. -// For all three tools, replacing an unaligned access with a tool-specific -// callback solves the problem. - -// Make sure uint16_t/uint32_t/uint64_t are defined. -#include - -extern "C" { -uint16_t __sanitizer_unaligned_load16(const void *p); -uint32_t __sanitizer_unaligned_load32(const void *p); -uint64_t __sanitizer_unaligned_load64(const void *p); -void __sanitizer_unaligned_store16(void *p, uint16_t v); -void __sanitizer_unaligned_store32(void *p, uint32_t v); -void __sanitizer_unaligned_store64(void *p, uint64_t v); -} // extern "C" - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace base_internal { - -inline uint16_t UnalignedLoad16(const void *p) { - return __sanitizer_unaligned_load16(p); -} - -inline uint32_t UnalignedLoad32(const void *p) { - return __sanitizer_unaligned_load32(p); -} - -inline uint64_t UnalignedLoad64(const void *p) { - return __sanitizer_unaligned_load64(p); -} - -inline void UnalignedStore16(void *p, uint16_t v) { - __sanitizer_unaligned_store16(p, v); -} - -inline void UnalignedStore32(void *p, uint32_t v) { - __sanitizer_unaligned_store32(p, v); -} - -inline void UnalignedStore64(void *p, uint64_t v) { - __sanitizer_unaligned_store64(p, v); -} - -} // namespace base_internal -ABSL_NAMESPACE_END -} // namespace absl - -#define ABSL_INTERNAL_UNALIGNED_LOAD16(_p) \ - (absl::base_internal::UnalignedLoad16(_p)) -#define ABSL_INTERNAL_UNALIGNED_LOAD32(_p) \ - (absl::base_internal::UnalignedLoad32(_p)) -#define ABSL_INTERNAL_UNALIGNED_LOAD64(_p) \ - (absl::base_internal::UnalignedLoad64(_p)) - -#define ABSL_INTERNAL_UNALIGNED_STORE16(_p, _val) \ - (absl::base_internal::UnalignedStore16(_p, _val)) -#define ABSL_INTERNAL_UNALIGNED_STORE32(_p, _val) \ - (absl::base_internal::UnalignedStore32(_p, _val)) -#define ABSL_INTERNAL_UNALIGNED_STORE64(_p, _val) \ - (absl::base_internal::UnalignedStore64(_p, _val)) - -#else - namespace absl { ABSL_NAMESPACE_BEGIN namespace base_internal { @@ -151,8 +77,6 @@ ABSL_NAMESPACE_END #define ABSL_INTERNAL_UNALIGNED_STORE64(_p, _val) \ (absl::base_internal::UnalignedStore64(_p, _val)) -#endif - #endif // defined(__cplusplus), end of unaligned API #endif // ABSL_BASE_INTERNAL_UNALIGNED_ACCESS_H_ diff --git a/Siv3D/include/ThirdParty/absl/base/internal/unscaledcycleclock.h b/Siv3D/include/ThirdParty/absl/base/internal/unscaledcycleclock.h index 82f2c87a95..cc1276ba08 100644 --- a/Siv3D/include/ThirdParty/absl/base/internal/unscaledcycleclock.h +++ b/Siv3D/include/ThirdParty/absl/base/internal/unscaledcycleclock.h @@ -42,49 +42,11 @@ #include #endif -#include "absl/base/port.h" - -// The following platforms have an implementation of a hardware counter. -#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || \ - defined(__powerpc__) || defined(__ppc__) || \ - defined(_M_IX86) || defined(_M_X64) -#define ABSL_HAVE_UNSCALED_CYCLECLOCK_IMPLEMENTATION 1 -#else -#define ABSL_HAVE_UNSCALED_CYCLECLOCK_IMPLEMENTATION 0 -#endif - -// The following platforms often disable access to the hardware -// counter (through a sandbox) even if the underlying hardware has a -// usable counter. The CycleTimer interface also requires a *scaled* -// CycleClock that runs at atleast 1 MHz. We've found some Android -// ARM64 devices where this is not the case, so we disable it by -// default on Android ARM64. -#if defined(__native_client__) || \ - (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) || \ - (defined(__ANDROID__) && defined(__aarch64__)) -#define ABSL_USE_UNSCALED_CYCLECLOCK_DEFAULT 0 -#else -#define ABSL_USE_UNSCALED_CYCLECLOCK_DEFAULT 1 -#endif - -// UnscaledCycleClock is an optional internal feature. -// Use "#if ABSL_USE_UNSCALED_CYCLECLOCK" to test for its presence. -// Can be overridden at compile-time via -DABSL_USE_UNSCALED_CYCLECLOCK=0|1 -#if !defined(ABSL_USE_UNSCALED_CYCLECLOCK) -#define ABSL_USE_UNSCALED_CYCLECLOCK \ - (ABSL_HAVE_UNSCALED_CYCLECLOCK_IMPLEMENTATION && \ - ABSL_USE_UNSCALED_CYCLECLOCK_DEFAULT) -#endif +#include "absl/base/config.h" +#include "absl/base/internal/unscaledcycleclock_config.h" #if ABSL_USE_UNSCALED_CYCLECLOCK -// This macro can be used to test if UnscaledCycleClock::Frequency() -// is NominalCPUFrequency() on a particular platform. -#if (defined(__i386__) || defined(__x86_64__) || \ - defined(_M_IX86) || defined(_M_X64)) -#define ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY -#endif - namespace absl { ABSL_NAMESPACE_BEGIN namespace time_internal { @@ -115,6 +77,16 @@ class UnscaledCycleClock { friend class base_internal::UnscaledCycleClockWrapperForInitializeFrequency; }; +#if defined(__x86_64__) + +inline int64_t UnscaledCycleClock::Now() { + uint64_t low, high; + __asm__ volatile("rdtsc" : "=a"(low), "=d"(high)); + return static_cast((high << 32) | low); +} + +#endif + } // namespace base_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/Siv3D/include/ThirdParty/absl/base/internal/unscaledcycleclock_config.h b/Siv3D/include/ThirdParty/absl/base/internal/unscaledcycleclock_config.h new file mode 100644 index 0000000000..24b324ac99 --- /dev/null +++ b/Siv3D/include/ThirdParty/absl/base/internal/unscaledcycleclock_config.h @@ -0,0 +1,62 @@ +// Copyright 2022 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_BASE_INTERNAL_UNSCALEDCYCLECLOCK_CONFIG_H_ +#define ABSL_BASE_INTERNAL_UNSCALEDCYCLECLOCK_CONFIG_H_ + +#if defined(__APPLE__) +#include +#endif + +// The following platforms have an implementation of a hardware counter. +#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || \ + defined(__powerpc__) || defined(__ppc__) || defined(__riscv) || \ + defined(_M_IX86) || (defined(_M_X64) && !defined(_M_ARM64EC)) +#define ABSL_HAVE_UNSCALED_CYCLECLOCK_IMPLEMENTATION 1 +#else +#define ABSL_HAVE_UNSCALED_CYCLECLOCK_IMPLEMENTATION 0 +#endif + +// The following platforms often disable access to the hardware +// counter (through a sandbox) even if the underlying hardware has a +// usable counter. The CycleTimer interface also requires a *scaled* +// CycleClock that runs at atleast 1 MHz. We've found some Android +// ARM64 devices where this is not the case, so we disable it by +// default on Android ARM64. +#if defined(__native_client__) || (defined(__APPLE__)) || \ + (defined(__ANDROID__) && defined(__aarch64__)) +#define ABSL_USE_UNSCALED_CYCLECLOCK_DEFAULT 0 +#else +#define ABSL_USE_UNSCALED_CYCLECLOCK_DEFAULT 1 +#endif + +// UnscaledCycleClock is an optional internal feature. +// Use "#if ABSL_USE_UNSCALED_CYCLECLOCK" to test for its presence. +// Can be overridden at compile-time via -DABSL_USE_UNSCALED_CYCLECLOCK=0|1 +#if !defined(ABSL_USE_UNSCALED_CYCLECLOCK) +#define ABSL_USE_UNSCALED_CYCLECLOCK \ + (ABSL_HAVE_UNSCALED_CYCLECLOCK_IMPLEMENTATION && \ + ABSL_USE_UNSCALED_CYCLECLOCK_DEFAULT) +#endif + +#if ABSL_USE_UNSCALED_CYCLECLOCK +// This macro can be used to test if UnscaledCycleClock::Frequency() +// is NominalCPUFrequency() on a particular platform. +#if (defined(__i386__) || defined(__x86_64__) || defined(__riscv) || \ + defined(_M_IX86) || defined(_M_X64)) +#define ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY +#endif +#endif + +#endif // ABSL_BASE_INTERNAL_UNSCALEDCYCLECLOCK_CONFIG_H_ diff --git a/Siv3D/include/ThirdParty/absl/base/log_severity.h b/Siv3D/include/ThirdParty/absl/base/log_severity.h index 65a3b16672..8bdca38b5f 100644 --- a/Siv3D/include/ThirdParty/absl/base/log_severity.h +++ b/Siv3D/include/ThirdParty/absl/base/log_severity.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef ABSL_BASE_INTERNAL_LOG_SEVERITY_H_ -#define ABSL_BASE_INTERNAL_LOG_SEVERITY_H_ +#ifndef ABSL_BASE_LOG_SEVERITY_H_ +#define ABSL_BASE_LOG_SEVERITY_H_ #include #include @@ -36,7 +36,7 @@ ABSL_NAMESPACE_BEGIN // such values to a defined severity level, however in some cases values other // than the defined levels are useful for comparison. // -// Exmaple: +// Example: // // // Effectively disables all logging: // SetMinLogLevel(static_cast(100)); @@ -115,7 +115,58 @@ constexpr absl::LogSeverity NormalizeLogSeverity(int s) { // unspecified; do not rely on it. std::ostream& operator<<(std::ostream& os, absl::LogSeverity s); +// Enums representing a lower bound for LogSeverity. APIs that only operate on +// messages of at least a certain level (for example, `SetMinLogLevel()`) use +// this type to specify that level. absl::LogSeverityAtLeast::kInfinity is +// a level above all threshold levels and therefore no log message will +// ever meet this threshold. +enum class LogSeverityAtLeast : int { + kInfo = static_cast(absl::LogSeverity::kInfo), + kWarning = static_cast(absl::LogSeverity::kWarning), + kError = static_cast(absl::LogSeverity::kError), + kFatal = static_cast(absl::LogSeverity::kFatal), + kInfinity = 1000, +}; + +std::ostream& operator<<(std::ostream& os, absl::LogSeverityAtLeast s); + +// Enums representing an upper bound for LogSeverity. APIs that only operate on +// messages of at most a certain level (for example, buffer all messages at or +// below a certain level) use this type to specify that level. +// absl::LogSeverityAtMost::kNegativeInfinity is a level below all threshold +// levels and therefore will exclude all log messages. +enum class LogSeverityAtMost : int { + kNegativeInfinity = -1000, + kInfo = static_cast(absl::LogSeverity::kInfo), + kWarning = static_cast(absl::LogSeverity::kWarning), + kError = static_cast(absl::LogSeverity::kError), + kFatal = static_cast(absl::LogSeverity::kFatal), +}; + +std::ostream& operator<<(std::ostream& os, absl::LogSeverityAtMost s); + +#define COMPOP(op1, op2, T) \ + constexpr bool operator op1(absl::T lhs, absl::LogSeverity rhs) { \ + return static_cast(lhs) op1 rhs; \ + } \ + constexpr bool operator op2(absl::LogSeverity lhs, absl::T rhs) { \ + return lhs op2 static_cast(rhs); \ + } + +// Comparisons between `LogSeverity` and `LogSeverityAtLeast`/ +// `LogSeverityAtMost` are only supported in one direction. +// Valid checks are: +// LogSeverity >= LogSeverityAtLeast +// LogSeverity < LogSeverityAtLeast +// LogSeverity <= LogSeverityAtMost +// LogSeverity > LogSeverityAtMost +COMPOP(>, <, LogSeverityAtLeast) +COMPOP(<=, >=, LogSeverityAtLeast) +COMPOP(<, >, LogSeverityAtMost) +COMPOP(>=, <=, LogSeverityAtMost) +#undef COMPOP + ABSL_NAMESPACE_END } // namespace absl -#endif // ABSL_BASE_INTERNAL_LOG_SEVERITY_H_ +#endif // ABSL_BASE_LOG_SEVERITY_H_ diff --git a/Siv3D/include/ThirdParty/absl/base/macros.h b/Siv3D/include/ThirdParty/absl/base/macros.h index 02dd9ff44f..f33cd1927b 100644 --- a/Siv3D/include/ThirdParty/absl/base/macros.h +++ b/Siv3D/include/ThirdParty/absl/base/macros.h @@ -103,17 +103,11 @@ ABSL_NAMESPACE_END // aborts the program in release mode (when NDEBUG is defined). The // implementation should abort the program as quickly as possible and ideally it // should not be possible to ignore the abort request. -#if (ABSL_HAVE_BUILTIN(__builtin_trap) && \ - ABSL_HAVE_BUILTIN(__builtin_unreachable)) || \ - (defined(__GNUC__) && !defined(__clang__)) -#define ABSL_INTERNAL_HARDENING_ABORT() \ - do { \ - __builtin_trap(); \ - __builtin_unreachable(); \ +#define ABSL_INTERNAL_HARDENING_ABORT() \ + do { \ + ABSL_INTERNAL_IMMEDIATE_ABORT_IMPL(); \ + ABSL_INTERNAL_UNREACHABLE_IMPL(); \ } while (false) -#else -#define ABSL_INTERNAL_HARDENING_ABORT() abort() -#endif // ABSL_HARDENING_ASSERT() // diff --git a/Siv3D/include/ThirdParty/absl/base/nullability.h b/Siv3D/include/ThirdParty/absl/base/nullability.h new file mode 100644 index 0000000000..6f49b6f535 --- /dev/null +++ b/Siv3D/include/ThirdParty/absl/base/nullability.h @@ -0,0 +1,224 @@ +// Copyright 2023 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: nullability.h +// ----------------------------------------------------------------------------- +// +// This header file defines a set of "templated annotations" for designating the +// expected nullability of pointers. These annotations allow you to designate +// pointers in one of three classification states: +// +// * "Non-null" (for pointers annotated `Nonnull`), indicating that it is +// invalid for the given pointer to ever be null. +// * "Nullable" (for pointers annotated `Nullable`), indicating that it is +// valid for the given pointer to be null. +// * "Unknown" (for pointers annotated `NullabilityUnknown`), indicating +// that the given pointer has not been yet classified as either nullable or +// non-null. This is the default state of unannotated pointers. +// +// NOTE: unannotated pointers implicitly bear the annotation +// `NullabilityUnknown`; you should rarely, if ever, see this annotation used +// in the codebase explicitly. +// +// ----------------------------------------------------------------------------- +// Nullability and Contracts +// ----------------------------------------------------------------------------- +// +// These nullability annotations allow you to more clearly specify contracts on +// software components by narrowing the *preconditions*, *postconditions*, and +// *invariants* of pointer state(s) in any given interface. It then depends on +// context who is responsible for fulfilling the annotation's requirements. +// +// For example, a function may receive a pointer argument. Designating that +// pointer argument as "non-null" tightens the precondition of the contract of +// that function. It is then the responsibility of anyone calling such a +// function to ensure that the passed pointer is not null. +// +// Similarly, a function may have a pointer as a return value. Designating that +// return value as "non-null" tightens the postcondition of the contract of that +// function. In this case, however, it is the responsibility of the function +// itself to ensure that the returned pointer is not null. +// +// Clearly defining these contracts allows providers (and consumers) of such +// pointers to have more confidence in their null state. If a function declares +// a return value as "non-null", for example, the caller should not need to +// check whether the returned value is `nullptr`; it can simply assume the +// pointer is valid. +// +// Of course most interfaces already have expectations on the nullability state +// of pointers, and these expectations are, in effect, a contract; often, +// however, those contracts are either poorly or partially specified, assumed, +// or misunderstood. These nullability annotations are designed to allow you to +// formalize those contracts within the codebase. +// +// ----------------------------------------------------------------------------- +// Using Nullability Annotations +// ----------------------------------------------------------------------------- +// +// It is important to note that these annotations are not distinct strong +// *types*. They are alias templates defined to be equal to the underlying +// pointer type. A pointer annotated `Nonnull`, for example, is simply a +// pointer of type `T*`. Each annotation acts as a form of documentation about +// the contract for the given pointer. Each annotation requires providers or +// consumers of these pointers across API boundaries to take appropriate steps +// when setting or using these pointers: +// +// * "Non-null" pointers should never be null. It is the responsibility of the +// provider of this pointer to ensure that the pointer may never be set to +// null. Consumers of such pointers can treat such pointers as non-null. +// * "Nullable" pointers may or may not be null. Consumers of such pointers +// should precede any usage of that pointer (e.g. a dereference operation) +// with a a `nullptr` check. +// * "Unknown" pointers may be either "non-null" or "nullable" but have not been +// definitively determined to be in either classification state. Providers of +// such pointers across API boundaries should determine -- over time -- to +// annotate the pointer in either of the above two states. Consumers of such +// pointers across an API boundary should continue to treat such pointers as +// they currently do. +// +// Example: +// +// // PaySalary() requires the passed pointer to an `Employee` to be non-null. +// void PaySalary(absl::Nonnull e) { +// pay(e->salary); // OK to dereference +// } +// +// // CompleteTransaction() guarantees the returned pointer to an `Account` to +// // be non-null. +// absl::Nonnull balance CompleteTransaction(double fee) { +// ... +// } +// +// // Note that specifying a nullability annotation does not prevent someone +// // from violating the contract: +// +// Nullable find(Map& employees, std::string_view name); +// +// void g(Map& employees) { +// Employee *e = find(employees, "Pat"); +// // `e` can now be null. +// PaySalary(e); // Violates contract, but compiles! +// } +// +// Nullability annotations, in other words, are useful for defining and +// narrowing contracts; *enforcement* of those contracts depends on use and any +// additional (static or dynamic analysis) tooling. +// +// NOTE: The "unknown" annotation state indicates that a pointer's contract has +// not yet been positively identified. The unknown state therefore acts as a +// form of documentation of your technical debt, and a codebase that adopts +// nullability annotations should aspire to annotate every pointer as either +// "non-null" or "nullable". +// +// ----------------------------------------------------------------------------- +// Applicability of Nullability Annotations +// ----------------------------------------------------------------------------- +// +// By default, nullability annotations are applicable to raw and smart +// pointers. User-defined types can indicate compatibility with nullability +// annotations by providing an `absl_nullability_compatible` nested type. The +// actual definition of this inner type is not relevant as it is used merely as +// a marker. It is common to use a using declaration of +// `absl_nullability_compatible` set to void. +// +// // Example: +// struct MyPtr { +// using absl_nullability_compatible = void; +// ... +// }; +// +// DISCLAIMER: +// =========================================================================== +// These nullability annotations are primarily a human readable signal about the +// intended contract of the pointer. They are not *types* and do not currently +// provide any correctness guarantees. For example, a pointer annotated as +// `Nonnull` is *not guaranteed* to be non-null, and the compiler won't +// alert or prevent assignment of a `Nullable` to a `Nonnull`. +// =========================================================================== +#ifndef ABSL_BASE_NULLABILITY_H_ +#define ABSL_BASE_NULLABILITY_H_ + +#include "absl/base/internal/nullability_impl.h" + +namespace absl { + +// absl::Nonnull +// +// The indicated pointer is never null. It is the responsibility of the provider +// of this pointer across an API boundary to ensure that the pointer is never be +// set to null. Consumers of this pointer across an API boundary may safely +// dereference the pointer. +// +// Example: +// +// // `employee` is designated as not null. +// void PaySalary(absl::Nonnull employee) { +// pay(*employee); // OK to dereference +// } +template +using Nonnull = nullability_internal::NonnullImpl; + +// absl::Nullable +// +// The indicated pointer may, by design, be either null or non-null. Consumers +// of this pointer across an API boundary should perform a `nullptr` check +// before performing any operation using the pointer. +// +// Example: +// +// // `employee` may be null. +// void PaySalary(absl::Nullable employee) { +// if (employee != nullptr) { +// Pay(*employee); // OK to dereference +// } +// } +template +using Nullable = nullability_internal::NullableImpl; + +// absl::NullabilityUnknown (default) +// +// The indicated pointer has not yet been determined to be definitively +// "non-null" or "nullable." Providers of such pointers across API boundaries +// should, over time, annotate such pointers as either "non-null" or "nullable." +// Consumers of these pointers across an API boundary should treat such pointers +// with the same caution they treat currently unannotated pointers. Most +// existing code will have "unknown" pointers, which should eventually be +// migrated into one of the above two nullability states: `Nonnull` or +// `Nullable`. +// +// NOTE: Because this annotation is the global default state, pointers without +// any annotation are assumed to have "unknown" semantics. This assumption is +// designed to minimize churn and reduce clutter within the codebase. +// +// Example: +// +// // `employee`s nullability state is unknown. +// void PaySalary(absl::NullabilityUnknown employee) { +// Pay(*employee); // Potentially dangerous. API provider should investigate. +// } +// +// Note that a pointer without an annotation, by default, is assumed to have the +// annotation `NullabilityUnknown`. +// +// // `employee`s nullability state is unknown. +// void PaySalary(Employee* employee) { +// Pay(*employee); // Potentially dangerous. API provider should investigate. +// } +template +using NullabilityUnknown = nullability_internal::NullabilityUnknownImpl; + +} // namespace absl + +#endif // ABSL_BASE_NULLABILITY_H_ diff --git a/Siv3D/include/ThirdParty/absl/base/optimization.h b/Siv3D/include/ThirdParty/absl/base/optimization.h index 2e31376cf6..ad0121ad3b 100644 --- a/Siv3D/include/ThirdParty/absl/base/optimization.h +++ b/Siv3D/include/ThirdParty/absl/base/optimization.h @@ -22,13 +22,15 @@ #ifndef ABSL_BASE_OPTIMIZATION_H_ #define ABSL_BASE_OPTIMIZATION_H_ +#include + #include "absl/base/config.h" // ABSL_BLOCK_TAIL_CALL_OPTIMIZATION // -// Instructs the compiler to avoid optimizing tail-call recursion. Use of this -// macro is useful when you wish to preserve the existing function order within -// a stack trace for logging, debugging, or profiling purposes. +// Instructs the compiler to avoid optimizing tail-call recursion. This macro is +// useful when you wish to preserve the existing function order within a stack +// trace for logging, debugging, or profiling purposes. // // Example: // @@ -89,6 +91,7 @@ #define ABSL_CACHELINE_SIZE 64 #endif #endif +#endif #ifndef ABSL_CACHELINE_SIZE // A reasonable default guess. Note that overestimates tend to waste more @@ -104,9 +107,10 @@ // Cacheline aligning objects properly allows constructive memory sharing and // prevents destructive (or "false") memory sharing. // -// NOTE: this macro should be replaced with usage of `alignas()` using +// NOTE: callers should replace uses of this macro with `alignas()` using // `std::hardware_constructive_interference_size` and/or -// `std::hardware_destructive_interference_size` when available within C++17. +// `std::hardware_destructive_interference_size` when C++17 becomes available to +// them. // // See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0154r1.html // for more information. @@ -138,12 +142,11 @@ // the generated machine code. // 3) Prefer applying this attribute to individual variables. Avoid // applying it to types. This tends to localize the effect. +#if defined(__clang__) || defined(__GNUC__) #define ABSL_CACHELINE_ALIGNED __attribute__((aligned(ABSL_CACHELINE_SIZE))) #elif defined(_MSC_VER) -#define ABSL_CACHELINE_SIZE 64 #define ABSL_CACHELINE_ALIGNED __declspec(align(ABSL_CACHELINE_SIZE)) #else -#define ABSL_CACHELINE_SIZE 64 #define ABSL_CACHELINE_ALIGNED #endif @@ -178,45 +181,105 @@ #define ABSL_PREDICT_TRUE(x) (x) #endif -// ABSL_INTERNAL_ASSUME(cond) -// Informs the compiler than a condition is always true and that it can assume -// it to be true for optimization purposes. The call has undefined behavior if -// the condition is false. +// `ABSL_INTERNAL_IMMEDIATE_ABORT_IMPL()` aborts the program in the fastest +// possible way, with no attempt at logging. One use is to implement hardening +// aborts with ABSL_OPTION_HARDENED. Since this is an internal symbol, it +// should not be used directly outside of Abseil. +#if ABSL_HAVE_BUILTIN(__builtin_trap) || \ + (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_INTERNAL_IMMEDIATE_ABORT_IMPL() __builtin_trap() +#else +#define ABSL_INTERNAL_IMMEDIATE_ABORT_IMPL() abort() +#endif + +// `ABSL_INTERNAL_UNREACHABLE_IMPL()` is the platform specific directive to +// indicate that a statement is unreachable, and to allow the compiler to +// optimize accordingly. Clients should use `ABSL_UNREACHABLE()`, which is +// defined below. +#if defined(__cpp_lib_unreachable) && __cpp_lib_unreachable >= 202202L +#define ABSL_INTERNAL_UNREACHABLE_IMPL() std::unreachable() +#elif defined(__GNUC__) || ABSL_HAVE_BUILTIN(__builtin_unreachable) +#define ABSL_INTERNAL_UNREACHABLE_IMPL() __builtin_unreachable() +#elif ABSL_HAVE_BUILTIN(__builtin_assume) +#define ABSL_INTERNAL_UNREACHABLE_IMPL() __builtin_assume(false) +#elif defined(_MSC_VER) +#define ABSL_INTERNAL_UNREACHABLE_IMPL() __assume(false) +#else +#define ABSL_INTERNAL_UNREACHABLE_IMPL() +#endif + +// `ABSL_UNREACHABLE()` is an unreachable statement. A program which reaches +// one has undefined behavior, and the compiler may optimize accordingly. +#if ABSL_OPTION_HARDENED == 1 && defined(NDEBUG) +// Abort in hardened mode to avoid dangerous undefined behavior. +#define ABSL_UNREACHABLE() \ + do { \ + ABSL_INTERNAL_IMMEDIATE_ABORT_IMPL(); \ + ABSL_INTERNAL_UNREACHABLE_IMPL(); \ + } while (false) +#else +// The assert only fires in debug mode to aid in debugging. +// When NDEBUG is defined, reaching ABSL_UNREACHABLE() is undefined behavior. +#define ABSL_UNREACHABLE() \ + do { \ + /* NOLINTNEXTLINE: misc-static-assert */ \ + assert(false && "ABSL_UNREACHABLE reached"); \ + ABSL_INTERNAL_UNREACHABLE_IMPL(); \ + } while (false) +#endif + +// ABSL_ASSUME(cond) +// +// Informs the compiler that a condition is always true and that it can assume +// it to be true for optimization purposes. +// +// WARNING: If the condition is false, the program can produce undefined and +// potentially dangerous behavior. +// // In !NDEBUG mode, the condition is checked with an assert(). -// NOTE: The expression must not have side effects, as it will only be evaluated -// in some compilation modes and not others. +// +// NOTE: The expression must not have side effects, as it may only be evaluated +// in some compilation modes and not others. Some compilers may issue a warning +// if the compiler cannot prove the expression has no side effects. For example, +// the expression should not use a function call since the compiler cannot prove +// that a function call does not have side effects. // // Example: // // int x = ...; -// ABSL_INTERNAL_ASSUME(x >= 0); +// ABSL_ASSUME(x >= 0); // // The compiler can optimize the division to a simple right shift using the // // assumption specified above. // int y = x / 16; // #if !defined(NDEBUG) -#define ABSL_INTERNAL_ASSUME(cond) assert(cond) +#define ABSL_ASSUME(cond) assert(cond) #elif ABSL_HAVE_BUILTIN(__builtin_assume) -#define ABSL_INTERNAL_ASSUME(cond) __builtin_assume(cond) +#define ABSL_ASSUME(cond) __builtin_assume(cond) +#elif defined(_MSC_VER) +#define ABSL_ASSUME(cond) __assume(cond) +#elif defined(__cpp_lib_unreachable) && __cpp_lib_unreachable >= 202202L +#define ABSL_ASSUME(cond) \ + do { \ + if (!(cond)) std::unreachable(); \ + } while (false) #elif defined(__GNUC__) || ABSL_HAVE_BUILTIN(__builtin_unreachable) -#define ABSL_INTERNAL_ASSUME(cond) \ +#define ABSL_ASSUME(cond) \ do { \ if (!(cond)) __builtin_unreachable(); \ - } while (0) -#elif defined(_MSC_VER) -#define ABSL_INTERNAL_ASSUME(cond) __assume(cond) + } while (false) #else -#define ABSL_INTERNAL_ASSUME(cond) \ +#define ABSL_ASSUME(cond) \ do { \ static_cast(false && (cond)); \ - } while (0) + } while (false) #endif // ABSL_INTERNAL_UNIQUE_SMALL_NAME(cond) // This macro forces small unique name on a static file level symbols like // static local variables or static functions. This is intended to be used in // macro definitions to optimize the cost of generated code. Do NOT use it on -// symbols exported from translation unit since it may casue a link time +// symbols exported from translation unit since it may cause a link time // conflict. // // Example: diff --git a/Siv3D/include/ThirdParty/absl/base/options.h b/Siv3D/include/ThirdParty/absl/base/options.h index 6a0fb07e5d..f308e1bf8b 100644 --- a/Siv3D/include/ThirdParty/absl/base/options.h +++ b/Siv3D/include/ThirdParty/absl/base/options.h @@ -67,12 +67,6 @@ #ifndef ABSL_BASE_OPTIONS_H_ #define ABSL_BASE_OPTIONS_H_ -// Include a standard library header to allow configuration based on the -// standard library in use. -#ifdef __cplusplus -#include -#endif - // ----------------------------------------------------------------------------- // Type Compatibility Options // ----------------------------------------------------------------------------- @@ -206,7 +200,7 @@ // allowed. #define ABSL_OPTION_USE_INLINE_NAMESPACE 1 -#define ABSL_OPTION_INLINE_NAMESPACE_NAME lts_2020_09_23 +#define ABSL_OPTION_INLINE_NAMESPACE_NAME lts_20230802 // ABSL_OPTION_HARDENED // diff --git a/Siv3D/include/ThirdParty/absl/base/policy_checks.h b/Siv3D/include/ThirdParty/absl/base/policy_checks.h index 06b3243916..372e848d8c 100644 --- a/Siv3D/include/ThirdParty/absl/base/policy_checks.h +++ b/Siv3D/include/ThirdParty/absl/base/policy_checks.h @@ -44,17 +44,17 @@ // Toolchain Check // ----------------------------------------------------------------------------- -// We support MSVC++ 14.0 update 2 and later. +// We support Visual Studio 2019 (MSVC++ 16.0) and later. // This minimum will go up. -#if defined(_MSC_FULL_VER) && _MSC_FULL_VER < 190023918 && !defined(__clang__) -#error "This package requires Visual Studio 2015 Update 2 or higher." +#if defined(_MSC_VER) && _MSC_VER < 1920 && !defined(__clang__) +#error "This package requires Visual Studio 2019 (MSVC++ 16.0) or higher." #endif -// We support gcc 4.7 and later. +// We support GCC 7 and later. // This minimum will go up. #if defined(__GNUC__) && !defined(__clang__) -#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 7) -#error "This package requires gcc 4.7 or higher." +#if __GNUC__ < 7 +#error "This package requires GCC 7 or higher." #endif #endif @@ -69,13 +69,15 @@ // C++ Version Check // ----------------------------------------------------------------------------- -// Enforce C++11 as the minimum. Note that Visual Studio has not -// advanced __cplusplus despite being good enough for our purposes, so -// so we exempt it from the check. -#if defined(__cplusplus) && !defined(_MSC_VER) -#if __cplusplus < 201103L -#error "C++ versions less than C++11 are not supported." -#endif +// Enforce C++14 as the minimum. +#if defined(_MSVC_LANG) +#if _MSVC_LANG < 201402L +#error "C++ versions less than C++14 are not supported." +#endif // _MSVC_LANG < 201402L +#elif defined(__cplusplus) +#if __cplusplus < 201402L +#error "C++ versions less than C++14 are not supported." +#endif // __cplusplus < 201402L #endif // ----------------------------------------------------------------------------- diff --git a/Siv3D/include/ThirdParty/absl/base/port.h b/Siv3D/include/ThirdParty/absl/base/port.h index 6c28068d4f..5bc4d6cd95 100644 --- a/Siv3D/include/ThirdParty/absl/base/port.h +++ b/Siv3D/include/ThirdParty/absl/base/port.h @@ -14,7 +14,6 @@ // // This files is a forwarding header for other headers containing various // portability macros and functions. -// This file is used for both C and C++! #ifndef ABSL_BASE_PORT_H_ #define ABSL_BASE_PORT_H_ diff --git a/Siv3D/include/ThirdParty/absl/base/prefetch.h b/Siv3D/include/ThirdParty/absl/base/prefetch.h new file mode 100644 index 0000000000..de7a180d60 --- /dev/null +++ b/Siv3D/include/ThirdParty/absl/base/prefetch.h @@ -0,0 +1,198 @@ +// Copyright 2023 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: prefetch.h +// ----------------------------------------------------------------------------- +// +// This header file defines prefetch functions to prefetch memory contents +// into the first level cache (L1) for the current CPU. The prefetch logic +// offered in this header is limited to prefetching first level cachelines +// only, and is aimed at relatively 'simple' prefetching logic. +// +#ifndef ABSL_BASE_PREFETCH_H_ +#define ABSL_BASE_PREFETCH_H_ + +#include "absl/base/config.h" + +#if defined(ABSL_INTERNAL_HAVE_SSE) +#include +#endif + +#if defined(_MSC_VER) && _MSC_VER >= 1900 && \ + (defined(_M_X64) || defined(_M_IX86)) +#include +#pragma intrinsic(_mm_prefetch) +#endif + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// Moves data into the L1 cache before it is read, or "prefetches" it. +// +// The value of `addr` is the address of the memory to prefetch. If +// the target and compiler support it, data prefetch instructions are +// generated. If the prefetch is done some time before the memory is +// read, it may be in the cache by the time the read occurs. +// +// This method prefetches data with the highest degree of temporal locality; +// data is prefetched where possible into all levels of the cache. +// +// Incorrect or gratuitous use of this function can degrade performance. +// Use this function only when representative benchmarks show an improvement. +// +// Example: +// +// // Computes incremental checksum for `data`. +// int ComputeChecksum(int sum, absl::string_view data); +// +// // Computes cumulative checksum for all values in `data` +// int ComputeChecksum(absl::Span data) { +// int sum = 0; +// auto it = data.begin(); +// auto pit = data.begin(); +// auto end = data.end(); +// for (int dist = 8; dist > 0 && pit != data.end(); --dist, ++pit) { +// absl::PrefetchToLocalCache(pit->data()); +// } +// for (; pit != end; ++pit, ++it) { +// sum = ComputeChecksum(sum, *it); +// absl::PrefetchToLocalCache(pit->data()); +// } +// for (; it != end; ++it) { +// sum = ComputeChecksum(sum, *it); +// } +// return sum; +// } +// +void PrefetchToLocalCache(const void* addr); + +// Moves data into the L1 cache before it is read, or "prefetches" it. +// +// This function is identical to `PrefetchToLocalCache()` except that it has +// non-temporal locality: the fetched data should not be left in any of the +// cache tiers. This is useful for cases where the data is used only once / +// short term, for example, invoking a destructor on an object. +// +// Incorrect or gratuitous use of this function can degrade performance. +// Use this function only when representative benchmarks show an improvement. +// +// Example: +// +// template +// void DestroyPointers(Iterator begin, Iterator end) { +// size_t distance = std::min(8U, bars.size()); +// +// int dist = 8; +// auto prefetch_it = begin; +// while (prefetch_it != end && --dist;) { +// absl::PrefetchToLocalCacheNta(*prefetch_it++); +// } +// while (prefetch_it != end) { +// delete *begin++; +// absl::PrefetchToLocalCacheNta(*prefetch_it++); +// } +// while (begin != end) { +// delete *begin++; +// } +// } +// +void PrefetchToLocalCacheNta(const void* addr); + +// Moves data into the L1 cache with the intent to modify it. +// +// This function is similar to `PrefetchToLocalCache()` except that it +// prefetches cachelines with an 'intent to modify' This typically includes +// invalidating cache entries for this address in all other cache tiers, and an +// exclusive access intent. +// +// Incorrect or gratuitous use of this function can degrade performance. As this +// function can invalidate cached cachelines on other caches and computer cores, +// incorrect usage of this function can have an even greater negative impact +// than incorrect regular prefetches. +// Use this function only when representative benchmarks show an improvement. +// +// Example: +// +// void* Arena::Allocate(size_t size) { +// void* ptr = AllocateBlock(size); +// absl::PrefetchToLocalCacheForWrite(p); +// return ptr; +// } +// +void PrefetchToLocalCacheForWrite(const void* addr); + +#if ABSL_HAVE_BUILTIN(__builtin_prefetch) || defined(__GNUC__) + +#define ABSL_HAVE_PREFETCH 1 + +// See __builtin_prefetch: +// https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html. +// +inline void PrefetchToLocalCache(const void* addr) { + __builtin_prefetch(addr, 0, 3); +} + +inline void PrefetchToLocalCacheNta(const void* addr) { + __builtin_prefetch(addr, 0, 0); +} + +inline void PrefetchToLocalCacheForWrite(const void* addr) { + // [x86] gcc/clang don't generate PREFETCHW for __builtin_prefetch(.., 1) + // unless -march=broadwell or newer; this is not generally the default, so we + // manually emit prefetchw. PREFETCHW is recognized as a no-op on older Intel + // processors and has been present on AMD processors since the K6-2. +#if defined(__x86_64__) + asm("prefetchw (%0)" : : "r"(addr)); +#else + __builtin_prefetch(addr, 1, 3); +#endif +} + +#elif defined(ABSL_INTERNAL_HAVE_SSE) + +#define ABSL_HAVE_PREFETCH 1 + +inline void PrefetchToLocalCache(const void* addr) { + _mm_prefetch(reinterpret_cast(addr), _MM_HINT_T0); +} + +inline void PrefetchToLocalCacheNta(const void* addr) { + _mm_prefetch(reinterpret_cast(addr), _MM_HINT_NTA); +} + +inline void PrefetchToLocalCacheForWrite(const void* addr) { +#if defined(_MM_HINT_ET0) + _mm_prefetch(reinterpret_cast(addr), _MM_HINT_ET0); +#elif !defined(_MSC_VER) && defined(__x86_64__) + // _MM_HINT_ET0 is not universally supported. As we commented further + // up, PREFETCHW is recognized as a no-op on older Intel processors + // and has been present on AMD processors since the K6-2. We have this + // disabled for MSVC compilers as this miscompiles on older MSVC compilers. + asm("prefetchw (%0)" : : "r"(addr)); +#endif +} + +#else + +inline void PrefetchToLocalCache(const void* addr) {} +inline void PrefetchToLocalCacheNta(const void* addr) {} +inline void PrefetchToLocalCacheForWrite(const void* addr) {} + +#endif + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_PREFETCH_H_ diff --git a/Siv3D/include/ThirdParty/absl/base/thread_annotations.h b/Siv3D/include/ThirdParty/absl/base/thread_annotations.h index e23fff1d20..bc8a620347 100644 --- a/Siv3D/include/ThirdParty/absl/base/thread_annotations.h +++ b/Siv3D/include/ThirdParty/absl/base/thread_annotations.h @@ -154,8 +154,8 @@ // ABSL_LOCKS_EXCLUDED() // -// Documents the locks acquired in the body of the function. These locks -// cannot be held when calling this function (as Abseil's `Mutex` locks are +// Documents the locks that cannot be held by callers of this function, as they +// might be acquired by this function (Abseil's `Mutex` locks are // non-reentrant). #if ABSL_HAVE_ATTRIBUTE(locks_excluded) #define ABSL_LOCKS_EXCLUDED(...) __attribute__((locks_excluded(__VA_ARGS__))) @@ -317,7 +317,7 @@ namespace base_internal { // Takes a reference to a guarded data member, and returns an unguarded // reference. -// Do not used this function directly, use ABSL_TS_UNCHECKED_READ instead. +// Do not use this function directly, use ABSL_TS_UNCHECKED_READ instead. template inline const T& ts_unchecked_read(const T& v) ABSL_NO_THREAD_SAFETY_ANALYSIS { return v; diff --git a/Siv3D/include/ThirdParty/absl/meta/type_traits.h b/Siv3D/include/ThirdParty/absl/meta/type_traits.h index 60849e0b15..cf71164b38 100644 --- a/Siv3D/include/ThirdParty/absl/meta/type_traits.h +++ b/Siv3D/include/ThirdParty/absl/meta/type_traits.h @@ -35,89 +35,31 @@ #ifndef ABSL_META_TYPE_TRAITS_H_ #define ABSL_META_TYPE_TRAITS_H_ -#include +#include #include #include +#include "absl/base/attributes.h" #include "absl/base/config.h" -// MSVC constructibility traits do not detect destructor properties and so our -// implementations should not use them as a source-of-truth. -#if defined(_MSC_VER) && !defined(__clang__) && !defined(__GNUC__) -#define ABSL_META_INTERNAL_STD_CONSTRUCTION_TRAITS_DONT_CHECK_DESTRUCTION 1 -#endif +// Defines the default alignment. `__STDCPP_DEFAULT_NEW_ALIGNMENT__` is a C++17 +// feature. +#if defined(__STDCPP_DEFAULT_NEW_ALIGNMENT__) +#define ABSL_INTERNAL_DEFAULT_NEW_ALIGNMENT __STDCPP_DEFAULT_NEW_ALIGNMENT__ +#else // defined(__STDCPP_DEFAULT_NEW_ALIGNMENT__) +#define ABSL_INTERNAL_DEFAULT_NEW_ALIGNMENT alignof(std::max_align_t) +#endif // defined(__STDCPP_DEFAULT_NEW_ALIGNMENT__) namespace absl { ABSL_NAMESPACE_BEGIN -// Defined and documented later on in this file. -template -struct is_trivially_destructible; - -// Defined and documented later on in this file. -template -struct is_trivially_move_assignable; - namespace type_traits_internal { -// Silence MSVC warnings about the destructor being defined as deleted. -#if defined(_MSC_VER) && !defined(__GNUC__) -#pragma warning(push) -#pragma warning(disable : 4624) -#endif // defined(_MSC_VER) && !defined(__GNUC__) - -template -union SingleMemberUnion { - T t; -}; - -// Restore the state of the destructor warning that was silenced above. -#if defined(_MSC_VER) && !defined(__GNUC__) -#pragma warning(pop) -#endif // defined(_MSC_VER) && !defined(__GNUC__) - -template -struct IsTriviallyMoveConstructibleObject - : std::integral_constant< - bool, std::is_move_constructible< - type_traits_internal::SingleMemberUnion>::value && - absl::is_trivially_destructible::value> {}; - -template -struct IsTriviallyCopyConstructibleObject - : std::integral_constant< - bool, std::is_copy_constructible< - type_traits_internal::SingleMemberUnion>::value && - absl::is_trivially_destructible::value> {}; - -template -struct IsTriviallyMoveAssignableReference : std::false_type {}; - -template -struct IsTriviallyMoveAssignableReference - : absl::is_trivially_move_assignable::type {}; - -template -struct IsTriviallyMoveAssignableReference - : absl::is_trivially_move_assignable::type {}; - template struct VoidTImpl { using type = void; }; -// This trick to retrieve a default alignment is necessary for our -// implementation of aligned_storage_t to be consistent with any implementation -// of std::aligned_storage. -template > -struct default_alignment_of_aligned_storage; - -template -struct default_alignment_of_aligned_storage> { - static constexpr size_t value = Align; -}; - //////////////////////////////// // Library Fundamentals V2 TS // //////////////////////////////// @@ -161,39 +103,8 @@ template class Op, class... Args> struct is_detected_convertible : is_detected_convertible_impl::type {}; -template -using IsCopyAssignableImpl = - decltype(std::declval() = std::declval()); - -template -using IsMoveAssignableImpl = decltype(std::declval() = std::declval()); - } // namespace type_traits_internal -// MSVC 19.20 has a regression that causes our workarounds to fail, but their -// std forms now appear to be compliant. -#if defined(_MSC_VER) && !defined(__clang__) && (_MSC_VER >= 1920) - -template -using is_copy_assignable = std::is_copy_assignable; - -template -using is_move_assignable = std::is_move_assignable; - -#else - -template -struct is_copy_assignable : type_traits_internal::is_detected< - type_traits_internal::IsCopyAssignableImpl, T> { -}; - -template -struct is_move_assignable : type_traits_internal::is_detected< - type_traits_internal::IsMoveAssignableImpl, T> { -}; - -#endif - // void_t() // // Ignores the type of any its arguments and returns `void`. In general, this @@ -274,273 +185,50 @@ struct is_function bool, !(std::is_reference::value || std::is_const::type>::value)> {}; +// is_copy_assignable() +// is_move_assignable() // is_trivially_destructible() -// -// Determines whether the passed type `T` is trivially destructible. -// -// This metafunction is designed to be a drop-in replacement for the C++11 -// `std::is_trivially_destructible()` metafunction for platforms that have -// incomplete C++11 support (such as libstdc++ 4.x). On any platforms that do -// fully support C++11, we check whether this yields the same result as the std -// implementation. -// -// NOTE: the extensions (__has_trivial_xxx) are implemented in gcc (version >= -// 4.3) and clang. Since we are supporting libstdc++ > 4.7, they should always -// be present. These extensions are documented at -// https://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html#Type-Traits. -template -struct is_trivially_destructible - : std::integral_constant::value> { -#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE - private: - static constexpr bool compliant = std::is_trivially_destructible::value == - is_trivially_destructible::value; - static_assert(compliant || std::is_trivially_destructible::value, - "Not compliant with std::is_trivially_destructible; " - "Standard: false, Implementation: true"); - static_assert(compliant || !std::is_trivially_destructible::value, - "Not compliant with std::is_trivially_destructible; " - "Standard: true, Implementation: false"); -#endif // ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE -}; - // is_trivially_default_constructible() -// -// Determines whether the passed type `T` is trivially default constructible. -// -// This metafunction is designed to be a drop-in replacement for the C++11 -// `std::is_trivially_default_constructible()` metafunction for platforms that -// have incomplete C++11 support (such as libstdc++ 4.x). On any platforms that -// do fully support C++11, we check whether this yields the same result as the -// std implementation. -// -// NOTE: according to the C++ standard, Section: 20.15.4.3 [meta.unary.prop] -// "The predicate condition for a template specialization is_constructible shall be satisfied if and only if the following variable -// definition would be well-formed for some invented variable t: -// -// T t(declval()...); -// -// is_trivially_constructible additionally requires that the -// variable definition does not call any operation that is not trivial. -// For the purposes of this check, the call to std::declval is considered -// trivial." -// -// Notes from https://en.cppreference.com/w/cpp/types/is_constructible: -// In many implementations, is_nothrow_constructible also checks if the -// destructor throws because it is effectively noexcept(T(arg)). Same -// applies to is_trivially_constructible, which, in these implementations, also -// requires that the destructor is trivial. -// GCC bug 51452: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51452 -// LWG issue 2116: http://cplusplus.github.io/LWG/lwg-active.html#2116. -// -// "T obj();" need to be well-formed and not call any nontrivial operation. -// Nontrivially destructible types will cause the expression to be nontrivial. -template -struct is_trivially_default_constructible - : std::integral_constant::value && - is_trivially_destructible::value> { -#if defined(ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE) && \ - !defined( \ - ABSL_META_INTERNAL_STD_CONSTRUCTION_TRAITS_DONT_CHECK_DESTRUCTION) - private: - static constexpr bool compliant = - std::is_trivially_default_constructible::value == - is_trivially_default_constructible::value; - static_assert(compliant || std::is_trivially_default_constructible::value, - "Not compliant with std::is_trivially_default_constructible; " - "Standard: false, Implementation: true"); - static_assert(compliant || !std::is_trivially_default_constructible::value, - "Not compliant with std::is_trivially_default_constructible; " - "Standard: true, Implementation: false"); -#endif // ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE -}; - // is_trivially_move_constructible() -// -// Determines whether the passed type `T` is trivially move constructible. -// -// This metafunction is designed to be a drop-in replacement for the C++11 -// `std::is_trivially_move_constructible()` metafunction for platforms that have -// incomplete C++11 support (such as libstdc++ 4.x). On any platforms that do -// fully support C++11, we check whether this yields the same result as the std -// implementation. -// -// NOTE: `T obj(declval());` needs to be well-formed and not call any -// nontrivial operation. Nontrivially destructible types will cause the -// expression to be nontrivial. -template -struct is_trivially_move_constructible - : std::conditional< - std::is_object::value && !std::is_array::value, - type_traits_internal::IsTriviallyMoveConstructibleObject, - std::is_reference>::type::type { -#if defined(ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE) && \ - !defined( \ - ABSL_META_INTERNAL_STD_CONSTRUCTION_TRAITS_DONT_CHECK_DESTRUCTION) - private: - static constexpr bool compliant = - std::is_trivially_move_constructible::value == - is_trivially_move_constructible::value; - static_assert(compliant || std::is_trivially_move_constructible::value, - "Not compliant with std::is_trivially_move_constructible; " - "Standard: false, Implementation: true"); - static_assert(compliant || !std::is_trivially_move_constructible::value, - "Not compliant with std::is_trivially_move_constructible; " - "Standard: true, Implementation: false"); -#endif // ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE -}; - // is_trivially_copy_constructible() -// -// Determines whether the passed type `T` is trivially copy constructible. -// -// This metafunction is designed to be a drop-in replacement for the C++11 -// `std::is_trivially_copy_constructible()` metafunction for platforms that have -// incomplete C++11 support (such as libstdc++ 4.x). On any platforms that do -// fully support C++11, we check whether this yields the same result as the std -// implementation. -// -// NOTE: `T obj(declval());` needs to be well-formed and not call any -// nontrivial operation. Nontrivially destructible types will cause the -// expression to be nontrivial. -template -struct is_trivially_copy_constructible - : std::conditional< - std::is_object::value && !std::is_array::value, - type_traits_internal::IsTriviallyCopyConstructibleObject, - std::is_lvalue_reference>::type::type { -#if defined(ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE) && \ - !defined( \ - ABSL_META_INTERNAL_STD_CONSTRUCTION_TRAITS_DONT_CHECK_DESTRUCTION) - private: - static constexpr bool compliant = - std::is_trivially_copy_constructible::value == - is_trivially_copy_constructible::value; - static_assert(compliant || std::is_trivially_copy_constructible::value, - "Not compliant with std::is_trivially_copy_constructible; " - "Standard: false, Implementation: true"); - static_assert(compliant || !std::is_trivially_copy_constructible::value, - "Not compliant with std::is_trivially_copy_constructible; " - "Standard: true, Implementation: false"); -#endif // ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE -}; - // is_trivially_move_assignable() -// -// Determines whether the passed type `T` is trivially move assignable. -// -// This metafunction is designed to be a drop-in replacement for the C++11 -// `std::is_trivially_move_assignable()` metafunction for platforms that have -// incomplete C++11 support (such as libstdc++ 4.x). On any platforms that do -// fully support C++11, we check whether this yields the same result as the std -// implementation. -// -// NOTE: `is_assignable::value` is `true` if the expression -// `declval() = declval()` is well-formed when treated as an unevaluated -// operand. `is_trivially_assignable` requires the assignment to call no -// operation that is not trivial. `is_trivially_copy_assignable` is simply -// `is_trivially_assignable`. -template -struct is_trivially_move_assignable - : std::conditional< - std::is_object::value && !std::is_array::value && - std::is_move_assignable::value, - std::is_move_assignable>, - type_traits_internal::IsTriviallyMoveAssignableReference>::type:: - type { -#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE - private: - static constexpr bool compliant = - std::is_trivially_move_assignable::value == - is_trivially_move_assignable::value; - static_assert(compliant || std::is_trivially_move_assignable::value, - "Not compliant with std::is_trivially_move_assignable; " - "Standard: false, Implementation: true"); - static_assert(compliant || !std::is_trivially_move_assignable::value, - "Not compliant with std::is_trivially_move_assignable; " - "Standard: true, Implementation: false"); -#endif // ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE -}; - // is_trivially_copy_assignable() // -// Determines whether the passed type `T` is trivially copy assignable. +// Historical note: Abseil once provided implementations of these type traits +// for platforms that lacked full support. New code should prefer to use the +// std variants. // -// This metafunction is designed to be a drop-in replacement for the C++11 -// `std::is_trivially_copy_assignable()` metafunction for platforms that have -// incomplete C++11 support (such as libstdc++ 4.x). On any platforms that do -// fully support C++11, we check whether this yields the same result as the std -// implementation. -// -// NOTE: `is_assignable::value` is `true` if the expression -// `declval() = declval()` is well-formed when treated as an unevaluated -// operand. `is_trivially_assignable` requires the assignment to call no -// operation that is not trivial. `is_trivially_copy_assignable` is simply -// `is_trivially_assignable`. +// See the documentation for the STL header for more information: +// https://en.cppreference.com/w/cpp/header/type_traits +using std::is_copy_assignable; +using std::is_move_assignable; +using std::is_trivially_copy_assignable; +using std::is_trivially_copy_constructible; +using std::is_trivially_default_constructible; +using std::is_trivially_destructible; +using std::is_trivially_move_assignable; +using std::is_trivially_move_constructible; + +#if defined(__cpp_lib_remove_cvref) && __cpp_lib_remove_cvref >= 201711L template -struct is_trivially_copy_assignable - : std::integral_constant< - bool, __has_trivial_assign(typename std::remove_reference::type) && - absl::is_copy_assignable::value> { -#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE - private: - static constexpr bool compliant = - std::is_trivially_copy_assignable::value == - is_trivially_copy_assignable::value; - static_assert(compliant || std::is_trivially_copy_assignable::value, - "Not compliant with std::is_trivially_copy_assignable; " - "Standard: false, Implementation: true"); - static_assert(compliant || !std::is_trivially_copy_assignable::value, - "Not compliant with std::is_trivially_copy_assignable; " - "Standard: true, Implementation: false"); -#endif // ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE -}; +using remove_cvref = std::remove_cvref; -namespace type_traits_internal { -// is_trivially_copyable() -// -// Determines whether the passed type `T` is trivially copyable. -// -// This metafunction is designed to be a drop-in replacement for the C++11 -// `std::is_trivially_copyable()` metafunction for platforms that have -// incomplete C++11 support (such as libstdc++ 4.x). We use the C++17 definition -// of TriviallyCopyable. -// -// NOTE: `is_trivially_copyable::value` is `true` if all of T's copy/move -// constructors/assignment operators are trivial or deleted, T has at least -// one non-deleted copy/move constructor/assignment operator, and T is trivially -// destructible. Arrays of trivially copyable types are trivially copyable. +template +using remove_cvref_t = typename std::remove_cvref::type; +#else +// remove_cvref() // -// We expose this metafunction only for internal use within absl. +// C++11 compatible implementation of std::remove_cvref which was added in +// C++20. template -class is_trivially_copyable_impl { - using ExtentsRemoved = typename std::remove_all_extents::type; - static constexpr bool kIsCopyOrMoveConstructible = - std::is_copy_constructible::value || - std::is_move_constructible::value; - static constexpr bool kIsCopyOrMoveAssignable = - absl::is_copy_assignable::value || - absl::is_move_assignable::value; - - public: - static constexpr bool kValue = - (__has_trivial_copy(ExtentsRemoved) || !kIsCopyOrMoveConstructible) && - (__has_trivial_assign(ExtentsRemoved) || !kIsCopyOrMoveAssignable) && - (kIsCopyOrMoveConstructible || kIsCopyOrMoveAssignable) && - is_trivially_destructible::value && - // We need to check for this explicitly because otherwise we'll say - // references are trivial copyable when compiled by MSVC. - !std::is_reference::value; +struct remove_cvref { + using type = + typename std::remove_cv::type>::type; }; template -struct is_trivially_copyable - : std::integral_constant< - bool, type_traits_internal::is_trivially_copyable_impl::kValue> {}; -} // namespace type_traits_internal +using remove_cvref_t = typename remove_cvref::type; +#endif // ----------------------------------------------------------------------------- // C++14 "_t" trait aliases @@ -591,9 +279,26 @@ using remove_extent_t = typename std::remove_extent::type; template using remove_all_extents_t = typename std::remove_all_extents::type; +ABSL_INTERNAL_DISABLE_DEPRECATED_DECLARATION_WARNING +namespace type_traits_internal { +// This trick to retrieve a default alignment is necessary for our +// implementation of aligned_storage_t to be consistent with any +// implementation of std::aligned_storage. +template > +struct default_alignment_of_aligned_storage; + +template +struct default_alignment_of_aligned_storage< + Len, std::aligned_storage> { + static constexpr size_t value = Align; +}; +} // namespace type_traits_internal + +// TODO(b/260219225): std::aligned_storage(_t) is deprecated in C++23. template ::value> using aligned_storage_t = typename std::aligned_storage::type; +ABSL_INTERNAL_RESTORE_DEPRECATED_DECLARATION_WARNING template using decay_t = typename std::decay::type; @@ -610,8 +315,23 @@ using common_type_t = typename std::common_type::type; template using underlying_type_t = typename std::underlying_type::type; -//template -//using result_of_t = typename std::result_of::type; + +namespace type_traits_internal { + +#if (defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703L) || \ + (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +// std::result_of is deprecated (C++17) or removed (C++20) +template struct result_of; +template +struct result_of : std::invoke_result {}; +#else +template using result_of = std::result_of; +#endif + +} // namespace type_traits_internal + +template +using result_of_t = typename type_traits_internal::result_of::type; namespace type_traits_internal { // In MSVC we can't probe std::hash or stdext::hash because it triggers a @@ -747,6 +467,97 @@ using swap_internal::Swap; using swap_internal::StdSwapIsUnconstrained; } // namespace type_traits_internal + +// absl::is_trivially_relocatable +// +// Detects whether a type is known to be "trivially relocatable" -- meaning it +// can be relocated without invoking the constructor/destructor, using a form of +// move elision. +// +// This trait is conservative, for backwards compatibility. If it's true then +// the type is definitely trivially relocatable, but if it's false then the type +// may or may not be. +// +// Example: +// +// if constexpr (absl::is_trivially_relocatable::value) { +// memcpy(new_location, old_location, sizeof(T)); +// } else { +// new(new_location) T(std::move(*old_location)); +// old_location->~T(); +// } +// +// Upstream documentation: +// +// https://clang.llvm.org/docs/LanguageExtensions.html#:~:text=__is_trivially_relocatable + +// If the compiler offers a builtin that tells us the answer, we can use that. +// This covers all of the cases in the fallback below, plus types that opt in +// using e.g. [[clang::trivial_abi]]. +// +// Clang on Windows has the builtin, but it falsely claims types with a +// user-provided destructor are trivial (http://b/275003464). So we opt out +// there. +// +// TODO(b/275003464): remove the opt-out once the bug is fixed. +// +// According to https://github.com/abseil/abseil-cpp/issues/1479, this does not +// work with NVCC either. +#if ABSL_HAVE_BUILTIN(__is_trivially_relocatable) && \ + !(defined(__clang__) && (defined(_WIN32) || defined(_WIN64))) && \ + !defined(__NVCC__) +template +struct is_trivially_relocatable + : std::integral_constant {}; +#else +// Otherwise we use a fallback that detects only those types we can feasibly +// detect. Any time that has trivial move-construction and destruction +// operations is by definition trivially relocatable. +template +struct is_trivially_relocatable + : absl::conjunction, + absl::is_trivially_destructible> {}; +#endif + +// absl::is_constant_evaluated() +// +// Detects whether the function call occurs within a constant-evaluated context. +// Returns true if the evaluation of the call occurs within the evaluation of an +// expression or conversion that is manifestly constant-evaluated; otherwise +// returns false. +// +// This function is implemented in terms of `std::is_constant_evaluated` for +// c++20 and up. For older c++ versions, the function is implemented in terms +// of `__builtin_is_constant_evaluated` if available, otherwise the function +// will fail to compile. +// +// Applications can inspect `ABSL_HAVE_CONSTANT_EVALUATED` at compile time +// to check if this function is supported. +// +// Example: +// +// constexpr MyClass::MyClass(int param) { +// #ifdef ABSL_HAVE_CONSTANT_EVALUATED +// if (!absl::is_constant_evaluated()) { +// ABSL_LOG(INFO) << "MyClass(" << param << ")"; +// } +// #endif // ABSL_HAVE_CONSTANT_EVALUATED +// } +// +// Upstream documentation: +// +// http://en.cppreference.com/w/cpp/types/is_constant_evaluated +// http://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html#:~:text=__builtin_is_constant_evaluated +// +#if defined(ABSL_HAVE_CONSTANT_EVALUATED) +constexpr bool is_constant_evaluated() noexcept { +#ifdef __cpp_lib_is_constant_evaluated + return std::is_constant_evaluated(); +#elif ABSL_HAVE_BUILTIN(__builtin_is_constant_evaluated) + return __builtin_is_constant_evaluated(); +#endif +} +#endif // ABSL_HAVE_CONSTANT_EVALUATED ABSL_NAMESPACE_END } // namespace absl diff --git a/Siv3D/include/ThirdParty/absl/numeric/bits.h b/Siv3D/include/ThirdParty/absl/numeric/bits.h new file mode 100644 index 0000000000..5ed36f5296 --- /dev/null +++ b/Siv3D/include/ThirdParty/absl/numeric/bits.h @@ -0,0 +1,177 @@ +// Copyright 2020 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: bits.h +// ----------------------------------------------------------------------------- +// +// This file contains implementations of C++20's bitwise math functions, as +// defined by: +// +// P0553R4: +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0553r4.html +// P0556R3: +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0556r3.html +// P1355R2: +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1355r2.html +// P1956R1: +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1956r1.pdf +// +// When using a standard library that implements these functions, we use the +// standard library's implementation. + +#ifndef ABSL_NUMERIC_BITS_H_ +#define ABSL_NUMERIC_BITS_H_ + +#include +#include +#include + +#include "absl/base/config.h" + +#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L +#include +#endif + +#include "absl/base/attributes.h" +#include "absl/numeric/internal/bits.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +#if !(defined(__cpp_lib_bitops) && __cpp_lib_bitops >= 201907L) + +// rotating +template +ABSL_MUST_USE_RESULT constexpr + typename std::enable_if::value, T>::type + rotl(T x, int s) noexcept { + return numeric_internal::RotateLeft(x, s); +} + +template +ABSL_MUST_USE_RESULT constexpr + typename std::enable_if::value, T>::type + rotr(T x, int s) noexcept { + return numeric_internal::RotateRight(x, s); +} + +// Counting functions +// +// While these functions are typically constexpr, on some platforms, they may +// not be marked as constexpr due to constraints of the compiler/available +// intrinsics. +template +ABSL_INTERNAL_CONSTEXPR_CLZ inline + typename std::enable_if::value, int>::type + countl_zero(T x) noexcept { + return numeric_internal::CountLeadingZeroes(x); +} + +template +ABSL_INTERNAL_CONSTEXPR_CLZ inline + typename std::enable_if::value, int>::type + countl_one(T x) noexcept { + // Avoid integer promotion to a wider type + return countl_zero(static_cast(~x)); +} + +template +ABSL_INTERNAL_CONSTEXPR_CTZ inline + typename std::enable_if::value, int>::type + countr_zero(T x) noexcept { + return numeric_internal::CountTrailingZeroes(x); +} + +template +ABSL_INTERNAL_CONSTEXPR_CTZ inline + typename std::enable_if::value, int>::type + countr_one(T x) noexcept { + // Avoid integer promotion to a wider type + return countr_zero(static_cast(~x)); +} + +template +ABSL_INTERNAL_CONSTEXPR_POPCOUNT inline + typename std::enable_if::value, int>::type + popcount(T x) noexcept { + return numeric_internal::Popcount(x); +} +#else // defined(__cpp_lib_bitops) && __cpp_lib_bitops >= 201907L + +using std::countl_one; +using std::countl_zero; +using std::countr_one; +using std::countr_zero; +using std::popcount; +using std::rotl; +using std::rotr; + +#endif + +#if !(defined(__cpp_lib_int_pow2) && __cpp_lib_int_pow2 >= 202002L) +// Returns: true if x is an integral power of two; false otherwise. +template +constexpr inline typename std::enable_if::value, bool>::type +has_single_bit(T x) noexcept { + return x != 0 && (x & (x - 1)) == 0; +} + +// Returns: If x == 0, 0; otherwise one plus the base-2 logarithm of x, with any +// fractional part discarded. +template +ABSL_INTERNAL_CONSTEXPR_CLZ inline + typename std::enable_if::value, int>::type + bit_width(T x) noexcept { + return std::numeric_limits::digits - countl_zero(x); +} + +// Returns: If x == 0, 0; otherwise the maximal value y such that +// has_single_bit(y) is true and y <= x. +template +ABSL_INTERNAL_CONSTEXPR_CLZ inline + typename std::enable_if::value, T>::type + bit_floor(T x) noexcept { + return x == 0 ? 0 : T{1} << (bit_width(x) - 1); +} + +// Returns: N, where N is the smallest power of 2 greater than or equal to x. +// +// Preconditions: N is representable as a value of type T. +template +ABSL_INTERNAL_CONSTEXPR_CLZ inline + typename std::enable_if::value, T>::type + bit_ceil(T x) { + // If T is narrower than unsigned, T{1} << bit_width will be promoted. We + // want to force it to wraparound so that bit_ceil of an invalid value are not + // core constant expressions. + // + // BitCeilNonPowerOf2 triggers an overflow in constexpr contexts if we would + // undergo promotion to unsigned but not fit the result into T without + // truncation. + return has_single_bit(x) ? T{1} << (bit_width(x) - 1) + : numeric_internal::BitCeilNonPowerOf2(x); +} +#else // defined(__cpp_lib_int_pow2) && __cpp_lib_int_pow2 >= 202002L + +using std::bit_ceil; +using std::bit_floor; +using std::bit_width; +using std::has_single_bit; + +#endif + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_NUMERIC_BITS_H_ diff --git a/Siv3D/include/ThirdParty/absl/numeric/int128.h b/Siv3D/include/ThirdParty/absl/numeric/int128.h index 0dd814a890..7530a79341 100644 --- a/Siv3D/include/ThirdParty/absl/numeric/int128.h +++ b/Siv3D/include/ThirdParty/absl/numeric/int128.h @@ -18,6 +18,10 @@ // ----------------------------------------------------------------------------- // // This header file defines 128-bit integer types, `uint128` and `int128`. +// +// TODO(absl-team): This module is inconsistent as many inline `uint128` methods +// are defined in this file, while many inline `int128` methods are defined in +// the `int128_*_intrinsic.inc` files. #ifndef ABSL_NUMERIC_INT128_H_ #define ABSL_NUMERIC_INT128_H_ @@ -28,6 +32,7 @@ #include #include #include +#include #include #include "absl/base/config.h" @@ -40,7 +45,7 @@ // builtin type. We need to make sure not to define operator wchar_t() // alongside operator unsigned short() in these instances. #define ABSL_INTERNAL_WCHAR_T __wchar_t -#if defined(_M_X64) +#if defined(_M_X64) && !defined(_M_ARM64EC) #include #pragma intrinsic(_umul128) #endif // defined(_M_X64) @@ -115,8 +120,8 @@ class #ifdef ABSL_HAVE_INTRINSIC_INT128 constexpr uint128(__int128 v); // NOLINT(runtime/explicit) constexpr uint128(unsigned __int128 v); // NOLINT(runtime/explicit) -#endif // ABSL_HAVE_INTRINSIC_INT128 - constexpr uint128(int128 v); // NOLINT(runtime/explicit) +#endif // ABSL_HAVE_INTRINSIC_INT128 + constexpr uint128(int128 v); // NOLINT(runtime/explicit) explicit uint128(float v); explicit uint128(double v); explicit uint128(long double v); @@ -213,9 +218,17 @@ class return H::combine(std::move(h), Uint128High64(v), Uint128Low64(v)); } + // Support for absl::StrCat() etc. + template + friend void AbslStringify(Sink& sink, uint128 v) { + sink.Append(v.ToString()); + } + private: constexpr uint128(uint64_t high, uint64_t low); + std::string ToString() const; + // TODO(strel) Update implementation to use __int128 once all users of // uint128 are fixed to not depend on alignof(uint128) == 8. Also add // alignas(16) to class definition to keep alignment consistent across @@ -282,9 +295,9 @@ class numeric_limits { #endif // ABSL_HAVE_INTRINSIC_INT128 static constexpr bool tinyness_before = false; - static constexpr absl::uint128 (min)() { return 0; } + static constexpr absl::uint128(min)() { return 0; } static constexpr absl::uint128 lowest() { return 0; } - static constexpr absl::uint128 (max)() { return absl::Uint128Max(); } + static constexpr absl::uint128(max)() { return absl::Uint128Max(); } static constexpr absl::uint128 epsilon() { return 0; } static constexpr absl::uint128 round_error() { return 0; } static constexpr absl::uint128 infinity() { return 0; } @@ -450,9 +463,17 @@ class int128 { return H::combine(std::move(h), Int128High64(v), Int128Low64(v)); } + // Support for absl::StrCat() etc. + template + friend void AbslStringify(Sink& sink, int128 v) { + sink.Append(v.ToString()); + } + private: constexpr int128(int64_t high, uint64_t low); + std::string ToString() const; + #if defined(ABSL_HAVE_INTRINSIC_INT128) __int128 v_; #else // ABSL_HAVE_INTRINSIC_INT128 @@ -517,9 +538,9 @@ class numeric_limits { #endif // ABSL_HAVE_INTRINSIC_INT128 static constexpr bool tinyness_before = false; - static constexpr absl::int128 (min)() { return absl::Int128Min(); } + static constexpr absl::int128(min)() { return absl::Int128Min(); } static constexpr absl::int128 lowest() { return absl::Int128Min(); } - static constexpr absl::int128 (max)() { return absl::Int128Max(); } + static constexpr absl::int128(max)() { return absl::Int128Max(); } static constexpr absl::int128 epsilon() { return 0; } static constexpr absl::int128 round_error() { return 0; } static constexpr absl::int128 infinity() { return 0; } @@ -557,9 +578,7 @@ inline uint128& uint128::operator=(unsigned long v) { } // NOLINTNEXTLINE(runtime/int) -inline uint128& uint128::operator=(long long v) { - return *this = uint128(v); -} +inline uint128& uint128::operator=(long long v) { return *this = uint128(v); } // NOLINTNEXTLINE(runtime/int) inline uint128& uint128::operator=(unsigned long long v) { @@ -567,25 +586,21 @@ inline uint128& uint128::operator=(unsigned long long v) { } #ifdef ABSL_HAVE_INTRINSIC_INT128 -inline uint128& uint128::operator=(__int128 v) { - return *this = uint128(v); -} +inline uint128& uint128::operator=(__int128 v) { return *this = uint128(v); } inline uint128& uint128::operator=(unsigned __int128 v) { return *this = uint128(v); } #endif // ABSL_HAVE_INTRINSIC_INT128 -inline uint128& uint128::operator=(int128 v) { - return *this = uint128(v); -} +inline uint128& uint128::operator=(int128 v) { return *this = uint128(v); } // Arithmetic operators. -uint128 operator<<(uint128 lhs, int amount); -uint128 operator>>(uint128 lhs, int amount); -uint128 operator+(uint128 lhs, uint128 rhs); -uint128 operator-(uint128 lhs, uint128 rhs); +constexpr uint128 operator<<(uint128 lhs, int amount); +constexpr uint128 operator>>(uint128 lhs, int amount); +constexpr uint128 operator+(uint128 lhs, uint128 rhs); +constexpr uint128 operator-(uint128 lhs, uint128 rhs); uint128 operator*(uint128 lhs, uint128 rhs); uint128 operator/(uint128 lhs, uint128 rhs); uint128 operator%(uint128 lhs, uint128 rhs); @@ -633,8 +648,7 @@ constexpr uint64_t Uint128High64(uint128 v) { return v.hi_; } #if defined(ABSL_IS_LITTLE_ENDIAN) -constexpr uint128::uint128(uint64_t high, uint64_t low) - : lo_{low}, hi_{high} {} +constexpr uint128::uint128(uint64_t high, uint64_t low) : lo_{low}, hi_{high} {} constexpr uint128::uint128(int v) : lo_{static_cast(v)}, @@ -666,8 +680,7 @@ constexpr uint128::uint128(int128 v) #elif defined(ABSL_IS_BIG_ENDIAN) -constexpr uint128::uint128(uint64_t high, uint64_t low) - : hi_{high}, lo_{low} {} +constexpr uint128::uint128(uint64_t high, uint64_t low) : hi_{high}, lo_{low} {} constexpr uint128::uint128(int v) : hi_{v < 0 ? (std::numeric_limits::max)() : 0}, @@ -782,16 +795,19 @@ inline uint128::operator long double() const { // Comparison operators. -inline bool operator==(uint128 lhs, uint128 rhs) { +constexpr bool operator==(uint128 lhs, uint128 rhs) { +#if defined(ABSL_HAVE_INTRINSIC_INT128) + return static_cast(lhs) == + static_cast(rhs); +#else return (Uint128Low64(lhs) == Uint128Low64(rhs) && Uint128High64(lhs) == Uint128High64(rhs)); +#endif } -inline bool operator!=(uint128 lhs, uint128 rhs) { - return !(lhs == rhs); -} +constexpr bool operator!=(uint128 lhs, uint128 rhs) { return !(lhs == rhs); } -inline bool operator<(uint128 lhs, uint128 rhs) { +constexpr bool operator<(uint128 lhs, uint128 rhs) { #ifdef ABSL_HAVE_INTRINSIC_INT128 return static_cast(lhs) < static_cast(rhs); @@ -802,118 +818,165 @@ inline bool operator<(uint128 lhs, uint128 rhs) { #endif } -inline bool operator>(uint128 lhs, uint128 rhs) { return rhs < lhs; } +constexpr bool operator>(uint128 lhs, uint128 rhs) { return rhs < lhs; } -inline bool operator<=(uint128 lhs, uint128 rhs) { return !(rhs < lhs); } +constexpr bool operator<=(uint128 lhs, uint128 rhs) { return !(rhs < lhs); } -inline bool operator>=(uint128 lhs, uint128 rhs) { return !(lhs < rhs); } +constexpr bool operator>=(uint128 lhs, uint128 rhs) { return !(lhs < rhs); } // Unary operators. -inline uint128 operator-(uint128 val) { - uint64_t hi = ~Uint128High64(val); - uint64_t lo = ~Uint128Low64(val) + 1; - if (lo == 0) ++hi; // carry - return MakeUint128(hi, lo); +constexpr inline uint128 operator+(uint128 val) { return val; } + +constexpr inline int128 operator+(int128 val) { return val; } + +constexpr uint128 operator-(uint128 val) { +#if defined(ABSL_HAVE_INTRINSIC_INT128) + return -static_cast(val); +#else + return MakeUint128( + ~Uint128High64(val) + static_cast(Uint128Low64(val) == 0), + ~Uint128Low64(val) + 1); +#endif } -inline bool operator!(uint128 val) { +constexpr inline bool operator!(uint128 val) { +#if defined(ABSL_HAVE_INTRINSIC_INT128) + return !static_cast(val); +#else return !Uint128High64(val) && !Uint128Low64(val); +#endif } // Logical operators. -inline uint128 operator~(uint128 val) { +constexpr inline uint128 operator~(uint128 val) { +#if defined(ABSL_HAVE_INTRINSIC_INT128) + return ~static_cast(val); +#else return MakeUint128(~Uint128High64(val), ~Uint128Low64(val)); +#endif } -inline uint128 operator|(uint128 lhs, uint128 rhs) { +constexpr inline uint128 operator|(uint128 lhs, uint128 rhs) { +#if defined(ABSL_HAVE_INTRINSIC_INT128) + return static_cast(lhs) | + static_cast(rhs); +#else return MakeUint128(Uint128High64(lhs) | Uint128High64(rhs), - Uint128Low64(lhs) | Uint128Low64(rhs)); + Uint128Low64(lhs) | Uint128Low64(rhs)); +#endif } -inline uint128 operator&(uint128 lhs, uint128 rhs) { +constexpr inline uint128 operator&(uint128 lhs, uint128 rhs) { +#if defined(ABSL_HAVE_INTRINSIC_INT128) + return static_cast(lhs) & + static_cast(rhs); +#else return MakeUint128(Uint128High64(lhs) & Uint128High64(rhs), - Uint128Low64(lhs) & Uint128Low64(rhs)); + Uint128Low64(lhs) & Uint128Low64(rhs)); +#endif } -inline uint128 operator^(uint128 lhs, uint128 rhs) { +constexpr inline uint128 operator^(uint128 lhs, uint128 rhs) { +#if defined(ABSL_HAVE_INTRINSIC_INT128) + return static_cast(lhs) ^ + static_cast(rhs); +#else return MakeUint128(Uint128High64(lhs) ^ Uint128High64(rhs), - Uint128Low64(lhs) ^ Uint128Low64(rhs)); + Uint128Low64(lhs) ^ Uint128Low64(rhs)); +#endif } inline uint128& uint128::operator|=(uint128 other) { - hi_ |= other.hi_; - lo_ |= other.lo_; + *this = *this | other; return *this; } inline uint128& uint128::operator&=(uint128 other) { - hi_ &= other.hi_; - lo_ &= other.lo_; + *this = *this & other; return *this; } inline uint128& uint128::operator^=(uint128 other) { - hi_ ^= other.hi_; - lo_ ^= other.lo_; + *this = *this ^ other; return *this; } // Arithmetic operators. -inline uint128 operator<<(uint128 lhs, int amount) { +constexpr uint128 operator<<(uint128 lhs, int amount) { #ifdef ABSL_HAVE_INTRINSIC_INT128 return static_cast(lhs) << amount; #else // uint64_t shifts of >= 64 are undefined, so we will need some // special-casing. - if (amount < 64) { - if (amount != 0) { - return MakeUint128( - (Uint128High64(lhs) << amount) | (Uint128Low64(lhs) >> (64 - amount)), - Uint128Low64(lhs) << amount); - } - return lhs; - } - return MakeUint128(Uint128Low64(lhs) << (amount - 64), 0); + return amount >= 64 ? MakeUint128(Uint128Low64(lhs) << (amount - 64), 0) + : amount == 0 ? lhs + : MakeUint128((Uint128High64(lhs) << amount) | + (Uint128Low64(lhs) >> (64 - amount)), + Uint128Low64(lhs) << amount); #endif } -inline uint128 operator>>(uint128 lhs, int amount) { +constexpr uint128 operator>>(uint128 lhs, int amount) { #ifdef ABSL_HAVE_INTRINSIC_INT128 return static_cast(lhs) >> amount; #else // uint64_t shifts of >= 64 are undefined, so we will need some // special-casing. - if (amount < 64) { - if (amount != 0) { - return MakeUint128(Uint128High64(lhs) >> amount, - (Uint128Low64(lhs) >> amount) | - (Uint128High64(lhs) << (64 - amount))); - } - return lhs; - } - return MakeUint128(0, Uint128High64(lhs) >> (amount - 64)); + return amount >= 64 ? MakeUint128(0, Uint128High64(lhs) >> (amount - 64)) + : amount == 0 ? lhs + : MakeUint128(Uint128High64(lhs) >> amount, + (Uint128Low64(lhs) >> amount) | + (Uint128High64(lhs) << (64 - amount))); #endif } -inline uint128 operator+(uint128 lhs, uint128 rhs) { - uint128 result = MakeUint128(Uint128High64(lhs) + Uint128High64(rhs), - Uint128Low64(lhs) + Uint128Low64(rhs)); - if (Uint128Low64(result) < Uint128Low64(lhs)) { // check for carry - return MakeUint128(Uint128High64(result) + 1, Uint128Low64(result)); - } - return result; +#if !defined(ABSL_HAVE_INTRINSIC_INT128) +namespace int128_internal { +constexpr uint128 AddResult(uint128 result, uint128 lhs) { + // check for carry + return (Uint128Low64(result) < Uint128Low64(lhs)) + ? MakeUint128(Uint128High64(result) + 1, Uint128Low64(result)) + : result; } +} // namespace int128_internal +#endif -inline uint128 operator-(uint128 lhs, uint128 rhs) { - uint128 result = MakeUint128(Uint128High64(lhs) - Uint128High64(rhs), - Uint128Low64(lhs) - Uint128Low64(rhs)); - if (Uint128Low64(lhs) < Uint128Low64(rhs)) { // check for carry - return MakeUint128(Uint128High64(result) - 1, Uint128Low64(result)); - } - return result; +constexpr uint128 operator+(uint128 lhs, uint128 rhs) { +#if defined(ABSL_HAVE_INTRINSIC_INT128) + return static_cast(lhs) + + static_cast(rhs); +#else + return int128_internal::AddResult( + MakeUint128(Uint128High64(lhs) + Uint128High64(rhs), + Uint128Low64(lhs) + Uint128Low64(rhs)), + lhs); +#endif +} + +#if !defined(ABSL_HAVE_INTRINSIC_INT128) +namespace int128_internal { +constexpr uint128 SubstructResult(uint128 result, uint128 lhs, uint128 rhs) { + // check for carry + return (Uint128Low64(lhs) < Uint128Low64(rhs)) + ? MakeUint128(Uint128High64(result) - 1, Uint128Low64(result)) + : result; +} +} // namespace int128_internal +#endif + +constexpr uint128 operator-(uint128 lhs, uint128 rhs) { +#if defined(ABSL_HAVE_INTRINSIC_INT128) + return static_cast(lhs) - + static_cast(rhs); +#else + return int128_internal::SubstructResult( + MakeUint128(Uint128High64(lhs) - Uint128High64(rhs), + Uint128Low64(lhs) - Uint128Low64(rhs)), + lhs, rhs); +#endif } inline uint128 operator*(uint128 lhs, uint128 rhs) { @@ -922,7 +985,7 @@ inline uint128 operator*(uint128 lhs, uint128 rhs) { // can be used for uint128 storage. return static_cast(lhs) * static_cast(rhs); -#elif defined(_MSC_VER) && defined(_M_X64) +#elif defined(_MSC_VER) && defined(_M_X64) && !defined(_M_ARM64EC) uint64_t carry; uint64_t low = _umul128(Uint128Low64(lhs), Uint128Low64(rhs), &carry); return MakeUint128(Uint128Low64(lhs) * Uint128High64(rhs) + @@ -943,6 +1006,18 @@ inline uint128 operator*(uint128 lhs, uint128 rhs) { #endif // ABSL_HAVE_INTRINSIC128 } +#if defined(ABSL_HAVE_INTRINSIC_INT128) +inline uint128 operator/(uint128 lhs, uint128 rhs) { + return static_cast(lhs) / + static_cast(rhs); +} + +inline uint128 operator%(uint128 lhs, uint128 rhs) { + return static_cast(lhs) % + static_cast(rhs); +} +#endif + // Increment/decrement operators. inline uint128 uint128::operator++(int) { @@ -972,27 +1047,19 @@ constexpr int128 MakeInt128(int64_t high, uint64_t low) { } // Assignment from integer types. -inline int128& int128::operator=(int v) { - return *this = int128(v); -} +inline int128& int128::operator=(int v) { return *this = int128(v); } -inline int128& int128::operator=(unsigned int v) { - return *this = int128(v); -} +inline int128& int128::operator=(unsigned int v) { return *this = int128(v); } inline int128& int128::operator=(long v) { // NOLINT(runtime/int) return *this = int128(v); } // NOLINTNEXTLINE(runtime/int) -inline int128& int128::operator=(unsigned long v) { - return *this = int128(v); -} +inline int128& int128::operator=(unsigned long v) { return *this = int128(v); } // NOLINTNEXTLINE(runtime/int) -inline int128& int128::operator=(long long v) { - return *this = int128(v); -} +inline int128& int128::operator=(long long v) { return *this = int128(v); } // NOLINTNEXTLINE(runtime/int) inline int128& int128::operator=(unsigned long long v) { @@ -1000,17 +1067,17 @@ inline int128& int128::operator=(unsigned long long v) { } // Arithmetic operators. - -int128 operator+(int128 lhs, int128 rhs); -int128 operator-(int128 lhs, int128 rhs); +constexpr int128 operator-(int128 v); +constexpr int128 operator+(int128 lhs, int128 rhs); +constexpr int128 operator-(int128 lhs, int128 rhs); int128 operator*(int128 lhs, int128 rhs); int128 operator/(int128 lhs, int128 rhs); int128 operator%(int128 lhs, int128 rhs); -int128 operator|(int128 lhs, int128 rhs); -int128 operator&(int128 lhs, int128 rhs); -int128 operator^(int128 lhs, int128 rhs); -int128 operator<<(int128 lhs, int amount); -int128 operator>>(int128 lhs, int amount); +constexpr int128 operator|(int128 lhs, int128 rhs); +constexpr int128 operator&(int128 lhs, int128 rhs); +constexpr int128 operator^(int128 lhs, int128 rhs); +constexpr int128 operator<<(int128 lhs, int amount); +constexpr int128 operator>>(int128 lhs, int amount); inline int128& int128::operator+=(int128 other) { *this = *this + other; @@ -1062,6 +1129,9 @@ inline int128& int128::operator>>=(int amount) { return *this; } +// Forward declaration for comparison operators. +constexpr bool operator!=(int128 lhs, int128 rhs); + namespace int128_internal { // Casts from unsigned to signed while preserving the underlying binary diff --git a/Siv3D/include/ThirdParty/absl/numeric/int128_have_intrinsic.inc b/Siv3D/include/ThirdParty/absl/numeric/int128_have_intrinsic.inc index d6c76dd320..6f1ac64414 100644 --- a/Siv3D/include/ThirdParty/absl/numeric/int128_have_intrinsic.inc +++ b/Siv3D/include/ThirdParty/absl/numeric/int128_have_intrinsic.inc @@ -155,16 +155,13 @@ constexpr int128::operator unsigned __int128() const { #if defined(__clang__) && !defined(__ppc64__) inline int128::operator float() const { return static_cast(v_); } -inline int128::operator double () const { return static_cast(v_); } +inline int128::operator double() const { return static_cast(v_); } inline int128::operator long double() const { return static_cast(v_); } #else // Clang on PowerPC -// Forward declaration for conversion operators to floating point types. -int128 operator-(int128 v); -bool operator!=(int128 lhs, int128 rhs); inline int128::operator float() const { // We must convert the absolute value and then negate as needed, because @@ -199,51 +196,45 @@ inline int128::operator long double() const { // Comparison operators. -inline bool operator==(int128 lhs, int128 rhs) { +constexpr bool operator==(int128 lhs, int128 rhs) { return static_cast<__int128>(lhs) == static_cast<__int128>(rhs); } -inline bool operator!=(int128 lhs, int128 rhs) { +constexpr bool operator!=(int128 lhs, int128 rhs) { return static_cast<__int128>(lhs) != static_cast<__int128>(rhs); } -inline bool operator<(int128 lhs, int128 rhs) { +constexpr bool operator<(int128 lhs, int128 rhs) { return static_cast<__int128>(lhs) < static_cast<__int128>(rhs); } -inline bool operator>(int128 lhs, int128 rhs) { +constexpr bool operator>(int128 lhs, int128 rhs) { return static_cast<__int128>(lhs) > static_cast<__int128>(rhs); } -inline bool operator<=(int128 lhs, int128 rhs) { +constexpr bool operator<=(int128 lhs, int128 rhs) { return static_cast<__int128>(lhs) <= static_cast<__int128>(rhs); } -inline bool operator>=(int128 lhs, int128 rhs) { +constexpr bool operator>=(int128 lhs, int128 rhs) { return static_cast<__int128>(lhs) >= static_cast<__int128>(rhs); } // Unary operators. -inline int128 operator-(int128 v) { - return -static_cast<__int128>(v); -} +constexpr int128 operator-(int128 v) { return -static_cast<__int128>(v); } -inline bool operator!(int128 v) { - return !static_cast<__int128>(v); -} +constexpr bool operator!(int128 v) { return !static_cast<__int128>(v); } -inline int128 operator~(int128 val) { - return ~static_cast<__int128>(val); -} +constexpr int128 operator~(int128 val) { return ~static_cast<__int128>(val); } // Arithmetic operators. -inline int128 operator+(int128 lhs, int128 rhs) { +constexpr int128 operator+(int128 lhs, int128 rhs) { return static_cast<__int128>(lhs) + static_cast<__int128>(rhs); } -inline int128 operator-(int128 lhs, int128 rhs) { +constexpr int128 operator-(int128 lhs, int128 rhs) { return static_cast<__int128>(lhs) - static_cast<__int128>(rhs); } @@ -281,22 +272,22 @@ inline int128& int128::operator--() { return *this; } -inline int128 operator|(int128 lhs, int128 rhs) { +constexpr int128 operator|(int128 lhs, int128 rhs) { return static_cast<__int128>(lhs) | static_cast<__int128>(rhs); } -inline int128 operator&(int128 lhs, int128 rhs) { +constexpr int128 operator&(int128 lhs, int128 rhs) { return static_cast<__int128>(lhs) & static_cast<__int128>(rhs); } -inline int128 operator^(int128 lhs, int128 rhs) { +constexpr int128 operator^(int128 lhs, int128 rhs) { return static_cast<__int128>(lhs) ^ static_cast<__int128>(rhs); } -inline int128 operator<<(int128 lhs, int amount) { +constexpr int128 operator<<(int128 lhs, int amount) { return static_cast<__int128>(lhs) << amount; } -inline int128 operator>>(int128 lhs, int amount) { +constexpr int128 operator>>(int128 lhs, int amount) { return static_cast<__int128>(lhs) >> amount; } diff --git a/Siv3D/include/ThirdParty/absl/numeric/int128_no_intrinsic.inc b/Siv3D/include/ThirdParty/absl/numeric/int128_no_intrinsic.inc index c753771ae7..6f5d83777c 100644 --- a/Siv3D/include/ThirdParty/absl/numeric/int128_no_intrinsic.inc +++ b/Siv3D/include/ThirdParty/absl/numeric/int128_no_intrinsic.inc @@ -23,8 +23,7 @@ constexpr int64_t Int128High64(int128 v) { return v.hi_; } #if defined(ABSL_IS_LITTLE_ENDIAN) -constexpr int128::int128(int64_t high, uint64_t low) : - lo_(low), hi_(high) {} +constexpr int128::int128(int64_t high, uint64_t low) : lo_(low), hi_(high) {} constexpr int128::int128(int v) : lo_{static_cast(v)}, hi_{v < 0 ? ~int64_t{0} : 0} {} @@ -44,8 +43,7 @@ constexpr int128::int128(uint128 v) #elif defined(ABSL_IS_BIG_ENDIAN) -constexpr int128::int128(int64_t high, uint64_t low) : - hi_{high}, lo_{low} {} +constexpr int128::int128(int64_t high, uint64_t low) : hi_{high}, lo_{low} {} constexpr int128::int128(int v) : hi_{v < 0 ? ~int64_t{0} : 0}, lo_{static_cast(v)} {} @@ -134,10 +132,6 @@ constexpr int128::operator unsigned long long() const { // NOLINT(runtime/int) return static_cast(lo_); // NOLINT(runtime/int) } -// Forward declaration for conversion operators to floating point types. -int128 operator-(int128 v); -bool operator!=(int128 lhs, int128 rhs); - inline int128::operator float() const { // We must convert the absolute value and then negate as needed, because // floating point types are typically sign-magnitude. Otherwise, the @@ -169,76 +163,80 @@ inline int128::operator long double() const { // Comparison operators. -inline bool operator==(int128 lhs, int128 rhs) { +constexpr bool operator==(int128 lhs, int128 rhs) { return (Int128Low64(lhs) == Int128Low64(rhs) && Int128High64(lhs) == Int128High64(rhs)); } -inline bool operator!=(int128 lhs, int128 rhs) { - return !(lhs == rhs); -} +constexpr bool operator!=(int128 lhs, int128 rhs) { return !(lhs == rhs); } -inline bool operator<(int128 lhs, int128 rhs) { +constexpr bool operator<(int128 lhs, int128 rhs) { return (Int128High64(lhs) == Int128High64(rhs)) ? (Int128Low64(lhs) < Int128Low64(rhs)) : (Int128High64(lhs) < Int128High64(rhs)); } -inline bool operator>(int128 lhs, int128 rhs) { +constexpr bool operator>(int128 lhs, int128 rhs) { return (Int128High64(lhs) == Int128High64(rhs)) ? (Int128Low64(lhs) > Int128Low64(rhs)) : (Int128High64(lhs) > Int128High64(rhs)); } -inline bool operator<=(int128 lhs, int128 rhs) { - return !(lhs > rhs); -} +constexpr bool operator<=(int128 lhs, int128 rhs) { return !(lhs > rhs); } -inline bool operator>=(int128 lhs, int128 rhs) { - return !(lhs < rhs); -} +constexpr bool operator>=(int128 lhs, int128 rhs) { return !(lhs < rhs); } // Unary operators. -inline int128 operator-(int128 v) { - int64_t hi = ~Int128High64(v); - uint64_t lo = ~Int128Low64(v) + 1; - if (lo == 0) ++hi; // carry - return MakeInt128(hi, lo); +constexpr int128 operator-(int128 v) { + return MakeInt128(~Int128High64(v) + (Int128Low64(v) == 0), + ~Int128Low64(v) + 1); } -inline bool operator!(int128 v) { +constexpr bool operator!(int128 v) { return !Int128Low64(v) && !Int128High64(v); } -inline int128 operator~(int128 val) { +constexpr int128 operator~(int128 val) { return MakeInt128(~Int128High64(val), ~Int128Low64(val)); } // Arithmetic operators. -inline int128 operator+(int128 lhs, int128 rhs) { - int128 result = MakeInt128(Int128High64(lhs) + Int128High64(rhs), - Int128Low64(lhs) + Int128Low64(rhs)); - if (Int128Low64(result) < Int128Low64(lhs)) { // check for carry - return MakeInt128(Int128High64(result) + 1, Int128Low64(result)); - } - return result; +namespace int128_internal { +constexpr int128 SignedAddResult(int128 result, int128 lhs) { + // check for carry + return (Int128Low64(result) < Int128Low64(lhs)) + ? MakeInt128(Int128High64(result) + 1, Int128Low64(result)) + : result; +} +} // namespace int128_internal +constexpr int128 operator+(int128 lhs, int128 rhs) { + return int128_internal::SignedAddResult( + MakeInt128(Int128High64(lhs) + Int128High64(rhs), + Int128Low64(lhs) + Int128Low64(rhs)), + lhs); } -inline int128 operator-(int128 lhs, int128 rhs) { - int128 result = MakeInt128(Int128High64(lhs) - Int128High64(rhs), - Int128Low64(lhs) - Int128Low64(rhs)); - if (Int128Low64(lhs) < Int128Low64(rhs)) { // check for carry - return MakeInt128(Int128High64(result) - 1, Int128Low64(result)); - } - return result; +namespace int128_internal { +constexpr int128 SignedSubstructResult(int128 result, int128 lhs, int128 rhs) { + // check for carry + return (Int128Low64(lhs) < Int128Low64(rhs)) + ? MakeInt128(Int128High64(result) - 1, Int128Low64(result)) + : result; +} +} // namespace int128_internal +constexpr int128 operator-(int128 lhs, int128 rhs) { + return int128_internal::SignedSubstructResult( + MakeInt128(Int128High64(lhs) - Int128High64(rhs), + Int128Low64(lhs) - Int128Low64(rhs)), + lhs, rhs); } inline int128 operator*(int128 lhs, int128 rhs) { - uint128 result = uint128(lhs) * rhs; - return MakeInt128(int128_internal::BitCastToSigned(Uint128High64(result)), - Uint128Low64(result)); + return MakeInt128( + int128_internal::BitCastToSigned(Uint128High64(uint128(lhs) * rhs)), + Uint128Low64(uint128(lhs) * rhs)); } inline int128 int128::operator++(int) { @@ -263,46 +261,68 @@ inline int128& int128::operator--() { return *this; } -inline int128 operator|(int128 lhs, int128 rhs) { +constexpr int128 operator|(int128 lhs, int128 rhs) { return MakeInt128(Int128High64(lhs) | Int128High64(rhs), Int128Low64(lhs) | Int128Low64(rhs)); } -inline int128 operator&(int128 lhs, int128 rhs) { +constexpr int128 operator&(int128 lhs, int128 rhs) { return MakeInt128(Int128High64(lhs) & Int128High64(rhs), Int128Low64(lhs) & Int128Low64(rhs)); } -inline int128 operator^(int128 lhs, int128 rhs) { +constexpr int128 operator^(int128 lhs, int128 rhs) { return MakeInt128(Int128High64(lhs) ^ Int128High64(rhs), Int128Low64(lhs) ^ Int128Low64(rhs)); } -inline int128 operator<<(int128 lhs, int amount) { - // uint64_t shifts of >= 64 are undefined, so we need some special-casing. - if (amount < 64) { - if (amount != 0) { - return MakeInt128( - (Int128High64(lhs) << amount) | - static_cast(Int128Low64(lhs) >> (64 - amount)), - Int128Low64(lhs) << amount); - } +constexpr int128 operator<<(int128 lhs, int amount) { + // int64_t shifts of >= 63 are undefined, so we need some special-casing. + assert(amount >= 0 && amount < 127); + if (amount <= 0) { return lhs; + } else if (amount < 63) { + return MakeInt128( + (Int128High64(lhs) << amount) | + static_cast(Int128Low64(lhs) >> (64 - amount)), + Int128Low64(lhs) << amount); + } else if (amount == 63) { + return MakeInt128(((Int128High64(lhs) << 32) << 31) | + static_cast(Int128Low64(lhs) >> 1), + (Int128Low64(lhs) << 32) << 31); + } else if (amount == 127) { + return MakeInt128(static_cast(Int128Low64(lhs) << 63), 0); + } else if (amount > 127) { + return MakeInt128(0, 0); + } else { + // amount >= 64 && amount < 127 + return MakeInt128(static_cast(Int128Low64(lhs) << (amount - 64)), + 0); } - return MakeInt128(static_cast(Int128Low64(lhs) << (amount - 64)), 0); -} - -inline int128 operator>>(int128 lhs, int amount) { - // uint64_t shifts of >= 64 are undefined, so we need some special-casing. - if (amount < 64) { - if (amount != 0) { - return MakeInt128( - Int128High64(lhs) >> amount, - (Int128Low64(lhs) >> amount) | - (static_cast(Int128High64(lhs)) << (64 - amount))); - } +} + +constexpr int128 operator>>(int128 lhs, int amount) { + // int64_t shifts of >= 63 are undefined, so we need some special-casing. + assert(amount >= 0 && amount < 127); + if (amount <= 0) { return lhs; + } else if (amount < 63) { + return MakeInt128( + Int128High64(lhs) >> amount, + Int128Low64(lhs) >> amount | static_cast(Int128High64(lhs)) + << (64 - amount)); + } else if (amount == 63) { + return MakeInt128((Int128High64(lhs) >> 32) >> 31, + static_cast(Int128High64(lhs) << 1) | + (Int128Low64(lhs) >> 32) >> 31); + + } else if (amount >= 127) { + return MakeInt128((Int128High64(lhs) >> 32) >> 31, + static_cast((Int128High64(lhs) >> 32) >> 31)); + } else { + // amount >= 64 && amount < 127 + return MakeInt128( + (Int128High64(lhs) >> 32) >> 31, + static_cast(Int128High64(lhs) >> (amount - 64))); } - return MakeInt128(0, - static_cast(Int128High64(lhs) >> (amount - 64))); } diff --git a/Siv3D/include/ThirdParty/absl/numeric/internal/bits.h b/Siv3D/include/ThirdParty/absl/numeric/internal/bits.h new file mode 100644 index 0000000000..bfef06bce1 --- /dev/null +++ b/Siv3D/include/ThirdParty/absl/numeric/internal/bits.h @@ -0,0 +1,358 @@ +// Copyright 2020 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_NUMERIC_INTERNAL_BITS_H_ +#define ABSL_NUMERIC_INTERNAL_BITS_H_ + +#include +#include +#include + +// Clang on Windows has __builtin_clzll; otherwise we need to use the +// windows intrinsic functions. +#if defined(_MSC_VER) && !defined(__clang__) +#include +#endif + +#include "absl/base/attributes.h" +#include "absl/base/config.h" + +#if defined(__GNUC__) && !defined(__clang__) +// GCC +#define ABSL_NUMERIC_INTERNAL_HAVE_BUILTIN_OR_GCC(x) 1 +#else +#define ABSL_NUMERIC_INTERNAL_HAVE_BUILTIN_OR_GCC(x) ABSL_HAVE_BUILTIN(x) +#endif + +#if ABSL_NUMERIC_INTERNAL_HAVE_BUILTIN_OR_GCC(__builtin_popcountl) && \ + ABSL_NUMERIC_INTERNAL_HAVE_BUILTIN_OR_GCC(__builtin_popcountll) +#define ABSL_INTERNAL_CONSTEXPR_POPCOUNT constexpr +#define ABSL_INTERNAL_HAS_CONSTEXPR_POPCOUNT 1 +#else +#define ABSL_INTERNAL_CONSTEXPR_POPCOUNT +#define ABSL_INTERNAL_HAS_CONSTEXPR_POPCOUNT 0 +#endif + +#if ABSL_NUMERIC_INTERNAL_HAVE_BUILTIN_OR_GCC(__builtin_clz) && \ + ABSL_NUMERIC_INTERNAL_HAVE_BUILTIN_OR_GCC(__builtin_clzll) +#define ABSL_INTERNAL_CONSTEXPR_CLZ constexpr +#define ABSL_INTERNAL_HAS_CONSTEXPR_CLZ 1 +#else +#define ABSL_INTERNAL_CONSTEXPR_CLZ +#define ABSL_INTERNAL_HAS_CONSTEXPR_CLZ 0 +#endif + +#if ABSL_NUMERIC_INTERNAL_HAVE_BUILTIN_OR_GCC(__builtin_ctz) && \ + ABSL_NUMERIC_INTERNAL_HAVE_BUILTIN_OR_GCC(__builtin_ctzll) +#define ABSL_INTERNAL_CONSTEXPR_CTZ constexpr +#define ABSL_INTERNAL_HAS_CONSTEXPR_CTZ 1 +#else +#define ABSL_INTERNAL_CONSTEXPR_CTZ +#define ABSL_INTERNAL_HAS_CONSTEXPR_CTZ 0 +#endif + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace numeric_internal { + +constexpr bool IsPowerOf2(unsigned int x) noexcept { + return x != 0 && (x & (x - 1)) == 0; +} + +template +ABSL_MUST_USE_RESULT ABSL_ATTRIBUTE_ALWAYS_INLINE constexpr T RotateRight( + T x, int s) noexcept { + static_assert(std::is_unsigned::value, "T must be unsigned"); + static_assert(IsPowerOf2(std::numeric_limits::digits), + "T must have a power-of-2 size"); + + return static_cast(x >> (s & (std::numeric_limits::digits - 1))) | + static_cast(x << ((-s) & (std::numeric_limits::digits - 1))); +} + +template +ABSL_MUST_USE_RESULT ABSL_ATTRIBUTE_ALWAYS_INLINE constexpr T RotateLeft( + T x, int s) noexcept { + static_assert(std::is_unsigned::value, "T must be unsigned"); + static_assert(IsPowerOf2(std::numeric_limits::digits), + "T must have a power-of-2 size"); + + return static_cast(x << (s & (std::numeric_limits::digits - 1))) | + static_cast(x >> ((-s) & (std::numeric_limits::digits - 1))); +} + +ABSL_ATTRIBUTE_ALWAYS_INLINE ABSL_INTERNAL_CONSTEXPR_POPCOUNT inline int +Popcount32(uint32_t x) noexcept { +#if ABSL_NUMERIC_INTERNAL_HAVE_BUILTIN_OR_GCC(__builtin_popcount) + static_assert(sizeof(unsigned int) == sizeof(x), + "__builtin_popcount does not take 32-bit arg"); + return __builtin_popcount(x); +#else + x -= ((x >> 1) & 0x55555555); + x = ((x >> 2) & 0x33333333) + (x & 0x33333333); + return static_cast((((x + (x >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24); +#endif +} + +ABSL_ATTRIBUTE_ALWAYS_INLINE ABSL_INTERNAL_CONSTEXPR_POPCOUNT inline int +Popcount64(uint64_t x) noexcept { +#if ABSL_NUMERIC_INTERNAL_HAVE_BUILTIN_OR_GCC(__builtin_popcountll) + static_assert(sizeof(unsigned long long) == sizeof(x), // NOLINT(runtime/int) + "__builtin_popcount does not take 64-bit arg"); + return __builtin_popcountll(x); +#else + x -= (x >> 1) & 0x5555555555555555ULL; + x = ((x >> 2) & 0x3333333333333333ULL) + (x & 0x3333333333333333ULL); + return static_cast( + (((x + (x >> 4)) & 0xF0F0F0F0F0F0F0FULL) * 0x101010101010101ULL) >> 56); +#endif +} + +template +ABSL_ATTRIBUTE_ALWAYS_INLINE ABSL_INTERNAL_CONSTEXPR_POPCOUNT inline int +Popcount(T x) noexcept { + static_assert(std::is_unsigned::value, "T must be unsigned"); + static_assert(IsPowerOf2(std::numeric_limits::digits), + "T must have a power-of-2 size"); + static_assert(sizeof(x) <= sizeof(uint64_t), "T is too large"); + return sizeof(x) <= sizeof(uint32_t) ? Popcount32(x) : Popcount64(x); +} + +ABSL_ATTRIBUTE_ALWAYS_INLINE ABSL_INTERNAL_CONSTEXPR_CLZ inline int +CountLeadingZeroes32(uint32_t x) { +#if ABSL_NUMERIC_INTERNAL_HAVE_BUILTIN_OR_GCC(__builtin_clz) + // Use __builtin_clz, which uses the following instructions: + // x86: bsr, lzcnt + // ARM64: clz + // PPC: cntlzd + + static_assert(sizeof(unsigned int) == sizeof(x), + "__builtin_clz does not take 32-bit arg"); + // Handle 0 as a special case because __builtin_clz(0) is undefined. + return x == 0 ? 32 : __builtin_clz(x); +#elif defined(_MSC_VER) && !defined(__clang__) + unsigned long result = 0; // NOLINT(runtime/int) + if (_BitScanReverse(&result, x)) { + return 31 - result; + } + return 32; +#else + int zeroes = 28; + if (x >> 16) { + zeroes -= 16; + x >>= 16; + } + if (x >> 8) { + zeroes -= 8; + x >>= 8; + } + if (x >> 4) { + zeroes -= 4; + x >>= 4; + } + return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[x] + zeroes; +#endif +} + +ABSL_ATTRIBUTE_ALWAYS_INLINE ABSL_INTERNAL_CONSTEXPR_CLZ inline int +CountLeadingZeroes16(uint16_t x) { +#if ABSL_HAVE_BUILTIN(__builtin_clzs) + static_assert(sizeof(unsigned short) == sizeof(x), // NOLINT(runtime/int) + "__builtin_clzs does not take 16-bit arg"); + return x == 0 ? 16 : __builtin_clzs(x); +#else + return CountLeadingZeroes32(x) - 16; +#endif +} + +ABSL_ATTRIBUTE_ALWAYS_INLINE ABSL_INTERNAL_CONSTEXPR_CLZ inline int +CountLeadingZeroes64(uint64_t x) { +#if ABSL_NUMERIC_INTERNAL_HAVE_BUILTIN_OR_GCC(__builtin_clzll) + // Use __builtin_clzll, which uses the following instructions: + // x86: bsr, lzcnt + // ARM64: clz + // PPC: cntlzd + static_assert(sizeof(unsigned long long) == sizeof(x), // NOLINT(runtime/int) + "__builtin_clzll does not take 64-bit arg"); + + // Handle 0 as a special case because __builtin_clzll(0) is undefined. + return x == 0 ? 64 : __builtin_clzll(x); +#elif defined(_MSC_VER) && !defined(__clang__) && \ + (defined(_M_X64) || defined(_M_ARM64)) + // MSVC does not have __buitin_clzll. Use _BitScanReverse64. + unsigned long result = 0; // NOLINT(runtime/int) + if (_BitScanReverse64(&result, x)) { + return 63 - result; + } + return 64; +#elif defined(_MSC_VER) && !defined(__clang__) + // MSVC does not have __buitin_clzll. Compose two calls to _BitScanReverse + unsigned long result = 0; // NOLINT(runtime/int) + if ((x >> 32) && + _BitScanReverse(&result, static_cast(x >> 32))) { + return 31 - result; + } + if (_BitScanReverse(&result, static_cast(x))) { + return 63 - result; + } + return 64; +#else + int zeroes = 60; + if (x >> 32) { + zeroes -= 32; + x >>= 32; + } + if (x >> 16) { + zeroes -= 16; + x >>= 16; + } + if (x >> 8) { + zeroes -= 8; + x >>= 8; + } + if (x >> 4) { + zeroes -= 4; + x >>= 4; + } + return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[x] + zeroes; +#endif +} + +template +ABSL_ATTRIBUTE_ALWAYS_INLINE ABSL_INTERNAL_CONSTEXPR_CLZ inline int +CountLeadingZeroes(T x) { + static_assert(std::is_unsigned::value, "T must be unsigned"); + static_assert(IsPowerOf2(std::numeric_limits::digits), + "T must have a power-of-2 size"); + static_assert(sizeof(T) <= sizeof(uint64_t), "T too large"); + return sizeof(T) <= sizeof(uint16_t) + ? CountLeadingZeroes16(static_cast(x)) - + (std::numeric_limits::digits - + std::numeric_limits::digits) + : (sizeof(T) <= sizeof(uint32_t) + ? CountLeadingZeroes32(static_cast(x)) - + (std::numeric_limits::digits - + std::numeric_limits::digits) + : CountLeadingZeroes64(x)); +} + +ABSL_ATTRIBUTE_ALWAYS_INLINE ABSL_INTERNAL_CONSTEXPR_CTZ inline int +CountTrailingZeroesNonzero32(uint32_t x) { +#if ABSL_NUMERIC_INTERNAL_HAVE_BUILTIN_OR_GCC(__builtin_ctz) + static_assert(sizeof(unsigned int) == sizeof(x), + "__builtin_ctz does not take 32-bit arg"); + return __builtin_ctz(x); +#elif defined(_MSC_VER) && !defined(__clang__) + unsigned long result = 0; // NOLINT(runtime/int) + _BitScanForward(&result, x); + return result; +#else + int c = 31; + x &= ~x + 1; + if (x & 0x0000FFFF) c -= 16; + if (x & 0x00FF00FF) c -= 8; + if (x & 0x0F0F0F0F) c -= 4; + if (x & 0x33333333) c -= 2; + if (x & 0x55555555) c -= 1; + return c; +#endif +} + +ABSL_ATTRIBUTE_ALWAYS_INLINE ABSL_INTERNAL_CONSTEXPR_CTZ inline int +CountTrailingZeroesNonzero64(uint64_t x) { +#if ABSL_NUMERIC_INTERNAL_HAVE_BUILTIN_OR_GCC(__builtin_ctzll) + static_assert(sizeof(unsigned long long) == sizeof(x), // NOLINT(runtime/int) + "__builtin_ctzll does not take 64-bit arg"); + return __builtin_ctzll(x); +#elif defined(_MSC_VER) && !defined(__clang__) && \ + (defined(_M_X64) || defined(_M_ARM64)) + unsigned long result = 0; // NOLINT(runtime/int) + _BitScanForward64(&result, x); + return result; +#elif defined(_MSC_VER) && !defined(__clang__) + unsigned long result = 0; // NOLINT(runtime/int) + if (static_cast(x) == 0) { + _BitScanForward(&result, static_cast(x >> 32)); + return result + 32; + } + _BitScanForward(&result, static_cast(x)); + return result; +#else + int c = 63; + x &= ~x + 1; + if (x & 0x00000000FFFFFFFF) c -= 32; + if (x & 0x0000FFFF0000FFFF) c -= 16; + if (x & 0x00FF00FF00FF00FF) c -= 8; + if (x & 0x0F0F0F0F0F0F0F0F) c -= 4; + if (x & 0x3333333333333333) c -= 2; + if (x & 0x5555555555555555) c -= 1; + return c; +#endif +} + +ABSL_ATTRIBUTE_ALWAYS_INLINE ABSL_INTERNAL_CONSTEXPR_CTZ inline int +CountTrailingZeroesNonzero16(uint16_t x) { +#if ABSL_HAVE_BUILTIN(__builtin_ctzs) + static_assert(sizeof(unsigned short) == sizeof(x), // NOLINT(runtime/int) + "__builtin_ctzs does not take 16-bit arg"); + return __builtin_ctzs(x); +#else + return CountTrailingZeroesNonzero32(x); +#endif +} + +template +ABSL_ATTRIBUTE_ALWAYS_INLINE ABSL_INTERNAL_CONSTEXPR_CTZ inline int +CountTrailingZeroes(T x) noexcept { + static_assert(std::is_unsigned::value, "T must be unsigned"); + static_assert(IsPowerOf2(std::numeric_limits::digits), + "T must have a power-of-2 size"); + static_assert(sizeof(T) <= sizeof(uint64_t), "T too large"); + return x == 0 ? std::numeric_limits::digits + : (sizeof(T) <= sizeof(uint16_t) + ? CountTrailingZeroesNonzero16(static_cast(x)) + : (sizeof(T) <= sizeof(uint32_t) + ? CountTrailingZeroesNonzero32( + static_cast(x)) + : CountTrailingZeroesNonzero64(x))); +} + +// If T is narrower than unsigned, T{1} << bit_width will be promoted. We +// want to force it to wraparound so that bit_ceil of an invalid value are not +// core constant expressions. +template +ABSL_ATTRIBUTE_ALWAYS_INLINE ABSL_INTERNAL_CONSTEXPR_CLZ inline + typename std::enable_if::value, T>::type + BitCeilPromotionHelper(T x, T promotion) { + return (T{1} << (x + promotion)) >> promotion; +} + +template +ABSL_ATTRIBUTE_ALWAYS_INLINE ABSL_INTERNAL_CONSTEXPR_CLZ inline + typename std::enable_if::value, T>::type + BitCeilNonPowerOf2(T x) { + // If T is narrower than unsigned, it undergoes promotion to unsigned when we + // shift. We calculate the number of bits added by the wider type. + return BitCeilPromotionHelper( + static_cast(std::numeric_limits::digits - CountLeadingZeroes(x)), + T{sizeof(T) >= sizeof(unsigned) ? 0 + : std::numeric_limits::digits - + std::numeric_limits::digits}); +} + +} // namespace numeric_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_NUMERIC_INTERNAL_BITS_H_ diff --git a/Siv3D/include/ThirdParty/absl/numeric/internal/representation.h b/Siv3D/include/ThirdParty/absl/numeric/internal/representation.h new file mode 100644 index 0000000000..82d332fdde --- /dev/null +++ b/Siv3D/include/ThirdParty/absl/numeric/internal/representation.h @@ -0,0 +1,55 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_NUMERIC_INTERNAL_REPRESENTATION_H_ +#define ABSL_NUMERIC_INTERNAL_REPRESENTATION_H_ + +#include + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace numeric_internal { + +// Returns true iff long double is represented as a pair of doubles added +// together. +inline constexpr bool IsDoubleDouble() { + // A double-double value always has exactly twice the precision of a double + // value--one double carries the high digits and one double carries the low + // digits. This property is not shared with any other common floating-point + // representation, so this test won't trigger false positives. For reference, + // this table gives the number of bits of precision of each common + // floating-point representation: + // + // type precision + // IEEE single 24 b + // IEEE double 53 + // x86 long double 64 + // double-double 106 + // IEEE quadruple 113 + // + // Note in particular that a quadruple-precision float has greater precision + // than a double-double float despite taking up the same amount of memory; the + // quad has more of its bits allocated to the mantissa than the double-double + // has. + return std::numeric_limits::digits == + 2 * std::numeric_limits::digits; +} + +} // namespace numeric_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_NUMERIC_INTERNAL_REPRESENTATION_H_ diff --git a/Siv3D/include/ThirdParty/absl/random/bernoulli_distribution.h b/Siv3D/include/ThirdParty/absl/random/bernoulli_distribution.h index 25bd0d5ca4..d81b6ae6b1 100644 --- a/Siv3D/include/ThirdParty/absl/random/bernoulli_distribution.h +++ b/Siv3D/include/ThirdParty/absl/random/bernoulli_distribution.h @@ -138,16 +138,16 @@ bool bernoulli_distribution::Generate(double p, // 64 bits. // // Second, `c` is constructed by first casting explicitly to a signed - // integer and then converting implicitly to an unsigned integer of the same + // integer and then casting explicitly to an unsigned integer of the same // size. This is done because the hardware conversion instructions produce // signed integers from double; if taken as a uint64_t the conversion would // be wrong for doubles greater than 2^63 (not relevant in this use-case). // If converted directly to an unsigned integer, the compiler would end up // emitting code to handle such large values that are not relevant due to // the known bounds on `c`. To avoid these extra instructions this - // implementation converts first to the signed type and then use the - // implicit conversion to unsigned (which is a no-op). - const uint64_t c = static_cast(p * kP32); + // implementation converts first to the signed type and then convert to + // unsigned (which is a no-op). + const uint64_t c = static_cast(static_cast(p * kP32)); const uint32_t v = fast_u32(g); // FAST PATH: this path fails with probability 1/2^32. Note that simply // returning v <= c would approximate P very well (up to an absolute error diff --git a/Siv3D/include/ThirdParty/absl/random/bit_gen_ref.h b/Siv3D/include/ThirdParty/absl/random/bit_gen_ref.h index 9555460fd4..e475221a15 100644 --- a/Siv3D/include/ThirdParty/absl/random/bit_gen_ref.h +++ b/Siv3D/include/ThirdParty/absl/random/bit_gen_ref.h @@ -24,6 +24,10 @@ #ifndef ABSL_RANDOM_BIT_GEN_REF_H_ #define ABSL_RANDOM_BIT_GEN_REF_H_ +#include +#include +#include + #include "absl/base/internal/fast_type_id.h" #include "absl/base/macros.h" #include "absl/meta/type_traits.h" diff --git a/Siv3D/include/ThirdParty/absl/random/discrete_distribution.h b/Siv3D/include/ThirdParty/absl/random/discrete_distribution.h index edae13f14e..171aa11a1e 100644 --- a/Siv3D/include/ThirdParty/absl/random/discrete_distribution.h +++ b/Siv3D/include/ThirdParty/absl/random/discrete_distribution.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Abseil Authors. +// Copyright 2017 The Abseil Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/Siv3D/include/ThirdParty/absl/random/distributions.h b/Siv3D/include/ThirdParty/absl/random/distributions.h index 31c79694e5..37fc3aa7fd 100644 --- a/Siv3D/include/ThirdParty/absl/random/distributions.h +++ b/Siv3D/include/ThirdParty/absl/random/distributions.h @@ -373,7 +373,7 @@ RealType Gaussian(URBG&& urbg, // NOLINT(runtime/references) template IntType LogUniform(URBG&& urbg, // NOLINT(runtime/references) IntType lo, IntType hi, IntType base = 2) { - static_assert(std::is_integral::value, + static_assert(random_internal::IsIntegral::value, "Template-argument 'IntType' must be an integral type, in " "absl::LogUniform(...)"); @@ -403,7 +403,7 @@ IntType LogUniform(URBG&& urbg, // NOLINT(runtime/references) template IntType Poisson(URBG&& urbg, // NOLINT(runtime/references) double mean = 1.0) { - static_assert(std::is_integral::value, + static_assert(random_internal::IsIntegral::value, "Template-argument 'IntType' must be an integral type, in " "absl::Poisson(...)"); @@ -435,7 +435,7 @@ template IntType Zipf(URBG&& urbg, // NOLINT(runtime/references) IntType hi = (std::numeric_limits::max)(), double q = 2.0, double v = 1.0) { - static_assert(std::is_integral::value, + static_assert(random_internal::IsIntegral::value, "Template-argument 'IntType' must be an integral type, in " "absl::Zipf(...)"); diff --git a/Siv3D/include/ThirdParty/absl/random/internal/distribution_caller.h b/Siv3D/include/ThirdParty/absl/random/internal/distribution_caller.h index fc81b787eb..0f162a4e29 100644 --- a/Siv3D/include/ThirdParty/absl/random/internal/distribution_caller.h +++ b/Siv3D/include/ThirdParty/absl/random/internal/distribution_caller.h @@ -18,6 +18,7 @@ #define ABSL_RANDOM_INTERNAL_DISTRIBUTION_CALLER_H_ #include +#include #include "absl/base/config.h" #include "absl/base/internal/fast_type_id.h" @@ -32,6 +33,8 @@ namespace random_internal { // to intercept such calls. template struct DistributionCaller { + static_assert(!std::is_pointer::value, + "You must pass a reference, not a pointer."); // SFINAE to detect whether the URBG type includes a member matching // bool InvokeMock(base_internal::FastTypeIdType, void*, void*). // diff --git a/Siv3D/include/ThirdParty/absl/random/internal/explicit_seed_seq.h b/Siv3D/include/ThirdParty/absl/random/internal/explicit_seed_seq.h index 6a743eaf46..25f791535f 100644 --- a/Siv3D/include/ThirdParty/absl/random/internal/explicit_seed_seq.h +++ b/Siv3D/include/ThirdParty/absl/random/internal/explicit_seed_seq.h @@ -23,6 +23,7 @@ #include #include "absl/base/config.h" +#include "absl/base/internal/endian.h" namespace absl { ABSL_NAMESPACE_BEGIN diff --git a/Siv3D/include/ThirdParty/absl/random/internal/fast_uniform_bits.h b/Siv3D/include/ThirdParty/absl/random/internal/fast_uniform_bits.h index 425aaf7d83..83ee5c0fea 100644 --- a/Siv3D/include/ThirdParty/absl/random/internal/fast_uniform_bits.h +++ b/Siv3D/include/ThirdParty/absl/random/internal/fast_uniform_bits.h @@ -22,6 +22,7 @@ #include "absl/base/config.h" #include "absl/meta/type_traits.h" +#include "absl/random/internal/traits.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -56,9 +57,10 @@ constexpr UIntType IntegerLog2(UIntType n) { // `PowerOfTwoVariate(urbg)`. template constexpr size_t NumBits() { - return RangeSize() == 0 - ? std::numeric_limits::digits - : IntegerLog2(RangeSize()); + return static_cast( + RangeSize() == 0 + ? std::numeric_limits::digits + : IntegerLog2(RangeSize())); } // Given a shift value `n`, constructs a mask with exactly the low `n` bits set. @@ -98,7 +100,7 @@ class FastUniformBits { result_type operator()(URBG& g); // NOLINT(runtime/references) private: - static_assert(std::is_unsigned::value, + static_assert(IsUnsigned::value, "Class-template FastUniformBits<> must be parameterized using " "an unsigned type."); @@ -150,7 +152,8 @@ FastUniformBits::Generate(URBG& g, // NOLINT(runtime/references) result_type r = static_cast(g() - kMin); for (size_t n = 1; n < kIters; ++n) { - r = (r << kShift) + static_cast(g() - kMin); + r = static_cast(r << kShift) + + static_cast(g() - kMin); } return r; } diff --git a/Siv3D/include/ThirdParty/absl/random/internal/fastmath.h b/Siv3D/include/ThirdParty/absl/random/internal/fastmath.h index 6baeb5a7c9..963b7690f1 100644 --- a/Siv3D/include/ThirdParty/absl/random/internal/fastmath.h +++ b/Siv3D/include/ThirdParty/absl/random/internal/fastmath.h @@ -22,27 +22,22 @@ #include #include -#include "absl/base/internal/bits.h" +#include "absl/numeric/bits.h" namespace absl { ABSL_NAMESPACE_BEGIN namespace random_internal { -// Returns the position of the first bit set. -inline int LeadingSetBit(uint64_t n) { - return 64 - base_internal::CountLeadingZeros64(n); -} - // Compute log2(n) using integer operations. // While std::log2 is more accurate than std::log(n) / std::log(2), for // very large numbers--those close to std::numeric_limits::max() - 2, // for instance--std::log2 rounds up rather than down, which introduces // definite skew in the results. inline int IntLog2Floor(uint64_t n) { - return (n <= 1) ? 0 : (63 - base_internal::CountLeadingZeros64(n)); + return (n <= 1) ? 0 : (63 - countl_zero(n)); } inline int IntLog2Ceil(uint64_t n) { - return (n <= 1) ? 0 : (64 - base_internal::CountLeadingZeros64(n - 1)); + return (n <= 1) ? 0 : (64 - countl_zero(n - 1)); } inline double StirlingLogFactorial(double n) { @@ -55,18 +50,6 @@ inline double StirlingLogFactorial(double n) { (1.0 / 360.0) * ninv * ninv * ninv; } -// Rotate value right. -// -// We only implement the uint32_t / uint64_t versions because -// 1) those are the only ones we use, and -// 2) those are the only ones where clang detects the rotate idiom correctly. -inline constexpr uint32_t rotr(uint32_t value, uint8_t bits) { - return (value >> (bits & 31)) | (value << ((-bits) & 31)); -} -inline constexpr uint64_t rotr(uint64_t value, uint8_t bits) { - return (value >> (bits & 63)) | (value << ((-bits) & 63)); -} - } // namespace random_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/Siv3D/include/ThirdParty/absl/random/internal/generate_real.h b/Siv3D/include/ThirdParty/absl/random/internal/generate_real.h index 20f6d20807..9a6f4005ce 100644 --- a/Siv3D/include/ThirdParty/absl/random/internal/generate_real.h +++ b/Siv3D/include/ThirdParty/absl/random/internal/generate_real.h @@ -23,8 +23,8 @@ #include #include -#include "absl/base/internal/bits.h" #include "absl/meta/type_traits.h" +#include "absl/numeric/bits.h" #include "absl/random/internal/fastmath.h" #include "absl/random/internal/traits.h" @@ -50,10 +50,10 @@ struct GenerateSignedTag {}; // inputs, otherwise it never returns 0. // // When a value in U(0,1) is required, use: -// Uniform64ToReal; +// GenerateRealFromBits; // // When a value in U(-1,1) is required, use: -// Uniform64ToReal; +// GenerateRealFromBits; // // This generates more distinct values than the mathematical equivalent // `U(0, 1) * 2.0 - 1.0`. @@ -78,7 +78,7 @@ inline RealType GenerateRealFromBits(uint64_t bits, int exp_bias = 0) { "GenerateRealFromBits must be parameterized by either float or double."); static_assert(sizeof(uint_type) == sizeof(real_type), - "Mismatched unsinged and real types."); + "Mismatched unsigned and real types."); static_assert((std::numeric_limits::is_iec559 && std::numeric_limits::radix == 2), @@ -120,17 +120,15 @@ inline RealType GenerateRealFromBits(uint64_t bits, int exp_bias = 0) { // Number of leading zeros is mapped to the exponent: 2^-clz // bits is 0..01xxxxxx. After shifting, we're left with 1xxx...0..0 - int clz = base_internal::CountLeadingZeros64(bits); + int clz = countl_zero(bits); bits <<= (IncludeZero ? clz : (clz & 63)); // remove 0-bits. exp -= clz; // set the exponent. bits >>= (63 - kExp); // Construct the 32-bit or 64-bit IEEE 754 floating-point value from // the individual fields: sign, exp, mantissa(bits). - uint_type val = - (std::is_same::value ? 0u : sign) | - (static_cast(exp) << kExp) | - (static_cast(bits) & kMask); + uint_type val = sign | (static_cast(exp) << kExp) | + (static_cast(bits) & kMask); // bit_cast to the output-type real_type result; diff --git a/Siv3D/include/ThirdParty/absl/random/internal/mock_helpers.h b/Siv3D/include/ThirdParty/absl/random/internal/mock_helpers.h index 9af27ab3a2..a7a97bfc39 100644 --- a/Siv3D/include/ThirdParty/absl/random/internal/mock_helpers.h +++ b/Siv3D/include/ThirdParty/absl/random/internal/mock_helpers.h @@ -18,6 +18,7 @@ #include #include +#include #include "absl/base/internal/fast_type_id.h" #include "absl/types/optional.h" @@ -80,6 +81,13 @@ class MockHelpers { } public: + // InvokeMock is private; this provides access for some specialized use cases. + template + static inline bool PrivateInvokeMock(URBG* urbg, IdType type, + void* args_tuple, void* result) { + return urbg->InvokeMock(type, args_tuple, result); + } + // Invoke a mock for the KeyT (may or may not be a signature). // // KeyT is used to generate a typeid-based lookup key for the mock. @@ -93,7 +101,7 @@ class MockHelpers { template static auto MaybeInvokeMock(URBG* urbg, Args&&... args) -> absl::optional::result_type> { - // Use function overloading to dispatch to the implemenation since + // Use function overloading to dispatch to the implementation since // more modern patterns (e.g. require + constexpr) are not supported in all // compiler configurations. return InvokeMockImpl::result_type, @@ -109,14 +117,14 @@ class MockHelpers { // The mocked function signature will be composed from KeyT as: // result_type(args...) template - static auto MockFor(MockURBG& m) -> decltype( - std::declval() - .template RegisterMock::result_type, - typename KeySignature::arg_tuple_type>( - std::declval())) { + static auto MockFor(MockURBG& m) + -> decltype(m.template RegisterMock< + typename KeySignature::result_type, + typename KeySignature::arg_tuple_type>( + m, std::declval())) { return m.template RegisterMock::result_type, typename KeySignature::arg_tuple_type>( - ::absl::base_internal::FastTypeId()); + m, ::absl::base_internal::FastTypeId()); } }; diff --git a/Siv3D/include/ThirdParty/absl/random/internal/mock_overload_set.h b/Siv3D/include/ThirdParty/absl/random/internal/mock_overload_set.h index dccc6cee67..0d9c6c120c 100644 --- a/Siv3D/include/ThirdParty/absl/random/internal/mock_overload_set.h +++ b/Siv3D/include/ThirdParty/absl/random/internal/mock_overload_set.h @@ -19,7 +19,6 @@ #include #include "gmock/gmock.h" -#include "gtest/gtest.h" #include "absl/random/internal/mock_helpers.h" #include "absl/random/mocking_bit_gen.h" @@ -45,10 +44,12 @@ struct MockSingleOverload { "Overload signature must have return type matching the " "distribution result_type."); using KeyT = Ret(DistrT, std::tuple); - auto gmock_Call( - absl::MockingBitGen& gen, // NOLINT(google-runtime-references) - const ::testing::Matcher&... matchers) + + template + auto gmock_Call(MockURBG& gen, const ::testing::Matcher&... matchers) -> decltype(MockHelpers::MockFor(gen).gmock_Call(matchers...)) { + static_assert(std::is_base_of::value, + "Mocking requires an absl::MockingBitGen"); return MockHelpers::MockFor(gen).gmock_Call(matchers...); } }; @@ -59,12 +60,14 @@ struct MockSingleOverload { "Overload signature must have return type matching the " "distribution result_type."); using KeyT = Ret(DistrT, std::tuple); - auto gmock_Call( - const ::testing::Matcher& matcher, - absl::MockingBitGen& gen, // NOLINT(google-runtime-references) - const ::testing::Matcher&... matchers) + + template + auto gmock_Call(const ::testing::Matcher& matcher, MockURBG& gen, + const ::testing::Matcher&... matchers) -> decltype(MockHelpers::MockFor(gen).gmock_Call(matcher, matchers...)) { + static_assert(std::is_base_of::value, + "Mocking requires an absl::MockingBitGen"); return MockHelpers::MockFor(gen).gmock_Call(matcher, matchers...); } }; diff --git a/Siv3D/include/ThirdParty/absl/random/internal/nonsecure_base.h b/Siv3D/include/ThirdParty/absl/random/internal/nonsecure_base.h index 730fa2ea12..c3b80335ae 100644 --- a/Siv3D/include/ThirdParty/absl/random/internal/nonsecure_base.h +++ b/Siv3D/include/ThirdParty/absl/random/internal/nonsecure_base.h @@ -17,28 +17,82 @@ #include #include -#include #include -#include -#include #include +#include #include #include "absl/base/macros.h" +#include "absl/container/inlined_vector.h" #include "absl/meta/type_traits.h" #include "absl/random/internal/pool_urbg.h" #include "absl/random/internal/salted_seed_seq.h" #include "absl/random/internal/seed_material.h" -#include "absl/types/optional.h" #include "absl/types/span.h" namespace absl { ABSL_NAMESPACE_BEGIN namespace random_internal { +// RandenPoolSeedSeq is a custom seed sequence type where generate() fills the +// provided buffer via the RandenPool entropy source. +class RandenPoolSeedSeq { + private: + struct ContiguousTag {}; + struct BufferTag {}; + + // Generate random unsigned values directly into the buffer. + template + void generate_impl(ContiguousTag, Contiguous begin, Contiguous end) { + const size_t n = static_cast(std::distance(begin, end)); + auto* a = &(*begin); + RandenPool::Fill( + absl::MakeSpan(reinterpret_cast(a), sizeof(*a) * n)); + } + + // Construct a buffer of size n and fill it with values, then copy + // those values into the seed iterators. + template + void generate_impl(BufferTag, RandomAccessIterator begin, + RandomAccessIterator end) { + const size_t n = std::distance(begin, end); + absl::InlinedVector data(n, 0); + RandenPool::Fill(absl::MakeSpan(data.begin(), data.end())); + std::copy(std::begin(data), std::end(data), begin); + } + + public: + using result_type = uint32_t; + + size_t size() { return 0; } + + template + void param(OutIterator) const {} + + template + void generate(RandomAccessIterator begin, RandomAccessIterator end) { + // RandomAccessIterator must be assignable from uint32_t + if (begin != end) { + using U = typename std::iterator_traits::value_type; + // ContiguousTag indicates the common case of a known contiguous buffer, + // which allows directly filling the buffer. In C++20, + // std::contiguous_iterator_tag provides a mechanism for testing this + // capability, however until Abseil's support requirements allow us to + // assume C++20, limit checks to a few common cases. + using TagType = absl::conditional_t< + (std::is_pointer::value || + std::is_same::iterator>::value), + ContiguousTag, BufferTag>; + + generate_impl(TagType{}, begin, end); + } + } +}; + // Each instance of NonsecureURBGBase will be seeded by variates produced // by a thread-unique URBG-instance. -template +template class NonsecureURBGBase { public: using result_type = typename URBG::result_type; @@ -85,49 +139,6 @@ class NonsecureURBGBase { } private: - // Seeder is a custom seed sequence type where generate() fills the provided - // buffer via the RandenPool entropy source. - struct Seeder { - using result_type = uint32_t; - - size_t size() { return 0; } - - template - void param(OutIterator) const {} - - template - void generate(RandomAccessIterator begin, RandomAccessIterator end) { - if (begin != end) { - // begin, end must be random access iterators assignable from uint32_t. - generate_impl( - std::integral_constant{}, - begin, end); - } - } - - // Commonly, generate is invoked with a pointer to a buffer which - // can be cast to a uint32_t. - template - void generate_impl(std::integral_constant, - RandomAccessIterator begin, RandomAccessIterator end) { - auto buffer = absl::MakeSpan(begin, end); - auto target = absl::MakeSpan(reinterpret_cast(buffer.data()), - buffer.size()); - RandenPool::Fill(target); - } - - // The non-uint32_t case should be uncommon, and involves an extra copy, - // filling the uint32_t buffer and then mixing into the output. - template - void generate_impl(std::integral_constant, - RandomAccessIterator begin, RandomAccessIterator end) { - const size_t n = std::distance(begin, end); - absl::InlinedVector data(n, 0); - RandenPool::Fill(absl::MakeSpan(data.begin(), data.end())); - std::copy(std::begin(data), std::end(data), begin); - } - }; - static URBG ConstructURBG() { Seeder seeder; return URBG(seeder); diff --git a/Siv3D/include/ThirdParty/absl/random/internal/pcg_engine.h b/Siv3D/include/ThirdParty/absl/random/internal/pcg_engine.h index 53c23fe1b4..e1f4ef3317 100644 --- a/Siv3D/include/ThirdParty/absl/random/internal/pcg_engine.h +++ b/Siv3D/include/ThirdParty/absl/random/internal/pcg_engine.h @@ -19,6 +19,7 @@ #include "absl/base/config.h" #include "absl/meta/type_traits.h" +#include "absl/numeric/bits.h" #include "absl/numeric/int128.h" #include "absl/random/internal/fastmath.h" #include "absl/random/internal/iostream_state_saver.h" @@ -220,48 +221,27 @@ class pcg_engine { template class pcg128_params { public: -#if ABSL_HAVE_INTRINSIC_INT128 - using state_type = __uint128_t; - static inline constexpr state_type make_u128(uint64_t a, uint64_t b) { - return (static_cast<__uint128_t>(a) << 64) | b; - } -#else using state_type = absl::uint128; - static inline constexpr state_type make_u128(uint64_t a, uint64_t b) { - return absl::MakeUint128(a, b); - } -#endif - static inline constexpr state_type multiplier() { - return make_u128(kMultA, kMultB); + return absl::MakeUint128(kMultA, kMultB); } static inline constexpr state_type increment() { - return make_u128(kIncA, kIncB); + return absl::MakeUint128(kIncA, kIncB); } }; // Implementation of the PCG xsl_rr_128_64 128-bit mixing function, which // accepts an input of state_type and mixes it into an output of result_type. struct pcg_xsl_rr_128_64 { -#if ABSL_HAVE_INTRINSIC_INT128 - using state_type = __uint128_t; -#else using state_type = absl::uint128; -#endif using result_type = uint64_t; inline uint64_t operator()(state_type state) { // This is equivalent to the xsl_rr_128_64 mixing function. -#if ABSL_HAVE_INTRINSIC_INT128 uint64_t rotate = static_cast(state >> 122u); state ^= state >> 64; uint64_t s = static_cast(state); -#else - uint64_t h = Uint128High64(state); - uint64_t rotate = h >> 58u; - uint64_t s = Uint128Low64(state) ^ h; -#endif - return random_internal::rotr(s, rotate); + return rotr(s, static_cast(rotate)); } }; @@ -281,8 +261,8 @@ struct pcg_xsh_rr_64_32 { using state_type = uint64_t; using result_type = uint32_t; inline uint32_t operator()(uint64_t state) { - return random_internal::rotr( - static_cast(((state >> 18) ^ state) >> 27), state >> 59); + return rotr(static_cast(((state >> 18) ^ state) >> 27), + state >> 59); } }; diff --git a/Siv3D/include/ThirdParty/absl/random/internal/platform.h b/Siv3D/include/ThirdParty/absl/random/internal/platform.h index bbdb4e6208..d779f481dd 100644 --- a/Siv3D/include/ThirdParty/absl/random/internal/platform.h +++ b/Siv3D/include/ThirdParty/absl/random/internal/platform.h @@ -131,7 +131,7 @@ // ABSL_RANDOM_INTERNAL_AES_DISPATCH indicates whether the currently active // platform has, or should use run-time dispatch for selecting the -// acclerated Randen implementation. +// accelerated Randen implementation. #define ABSL_RANDOM_INTERNAL_AES_DISPATCH 0 #if defined(ABSL_ARCH_X86_64) diff --git a/Siv3D/include/ThirdParty/absl/random/internal/randen.h b/Siv3D/include/ThirdParty/absl/random/internal/randen.h index c2834aaf3d..9ff4a7a554 100644 --- a/Siv3D/include/ThirdParty/absl/random/internal/randen.h +++ b/Siv3D/include/ThirdParty/absl/random/internal/randen.h @@ -26,7 +26,7 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace random_internal { -// RANDen = RANDom generator or beetroots in Swiss German. +// RANDen = RANDom generator or beetroots in Swiss High German. // 'Strong' (well-distributed, unpredictable, backtracking-resistant) random // generator, faster in some benchmarks than std::mt19937_64 and pcg64_c32. // @@ -43,10 +43,8 @@ class Randen { // Generate updates the randen sponge. The outer portion of the sponge // (kCapacityBytes .. kStateBytes) may be consumed as PRNG state. - template - void Generate(T (&state)[N]) const { - static_assert(N * sizeof(T) == kStateBytes, - "Randen::Generate() requires kStateBytes of state"); + // REQUIRES: state points to kStateBytes of state. + inline void Generate(void* state) const { #if ABSL_RANDOM_INTERNAL_AES_DISPATCH // HW AES Dispatch. if (has_crypto_) { @@ -65,13 +63,9 @@ class Randen { // Absorb incorporates additional seed material into the randen sponge. After // absorb returns, Generate must be called before the state may be consumed. - template - void Absorb(const S (&seed)[M], T (&state)[N]) const { - static_assert(M * sizeof(S) == RandenTraits::kSeedBytes, - "Randen::Absorb() requires kSeedBytes of seed"); - - static_assert(N * sizeof(T) == RandenTraits::kStateBytes, - "Randen::Absorb() requires kStateBytes of state"); + // REQUIRES: seed points to kSeedBytes of seed. + // REQUIRES: state points to kStateBytes of state. + inline void Absorb(const void* seed, void* state) const { #if ABSL_RANDOM_INTERNAL_AES_DISPATCH // HW AES Dispatch. if (has_crypto_) { diff --git a/Siv3D/include/ThirdParty/absl/random/internal/randen_engine.h b/Siv3D/include/ThirdParty/absl/random/internal/randen_engine.h index 6b33731336..fe2d9f6c15 100644 --- a/Siv3D/include/ThirdParty/absl/random/internal/randen_engine.h +++ b/Siv3D/include/ThirdParty/absl/random/internal/randen_engine.h @@ -23,6 +23,7 @@ #include #include +#include "absl/base/internal/endian.h" #include "absl/meta/type_traits.h" #include "absl/random/internal/iostream_state_saver.h" #include "absl/random/internal/randen.h" @@ -41,7 +42,7 @@ namespace random_internal { // 'Strong' (well-distributed, unpredictable, backtracking-resistant) random // generator, faster in some benchmarks than std::mt19937_64 and pcg64_c32. template -class alignas(16) randen_engine { +class alignas(8) randen_engine { public: // C++11 URBG interface: using result_type = T; @@ -57,7 +58,8 @@ class alignas(16) randen_engine { return (std::numeric_limits::max)(); } - explicit randen_engine(result_type seed_value = 0) { seed(seed_value); } + randen_engine() : randen_engine(0) {} + explicit randen_engine(result_type seed_value) { seed(seed_value); } template = kStateSizeT) { next_ = kCapacityT; - impl_.Generate(state_); + impl_.Generate(begin); } - - return state_[next_++]; + return little_endian::ToHost(begin[next_++]); } template @@ -91,9 +103,10 @@ class alignas(16) randen_engine { void seed(result_type seed_value = 0) { next_ = kStateSizeT; // Zeroes the inner state and fills the outer state with seed_value to - // mimics behaviour of reseed - std::fill(std::begin(state_), std::begin(state_) + kCapacityT, 0); - std::fill(std::begin(state_) + kCapacityT, std::end(state_), seed_value); + // mimic the behaviour of reseed + auto* begin = state(); + std::fill(begin, begin + kCapacityT, 0); + std::fill(begin + kCapacityT, begin + kStateSizeT, seed_value); } // Inserts entropy into (part of) the state. Calling this periodically with @@ -104,7 +117,6 @@ class alignas(16) randen_engine { using sequence_result_type = typename SeedSequence::result_type; static_assert(sizeof(sequence_result_type) == 4, "SeedSequence::result_type must be 32-bit"); - constexpr size_t kBufferSize = Randen::kSeedBytes / sizeof(sequence_result_type); alignas(16) sequence_result_type buffer[kBufferSize]; @@ -118,12 +130,19 @@ class alignas(16) randen_engine { if (entropy_size < kBufferSize) { // ... and only request that many values, or 256-bits, when unspecified. const size_t requested_entropy = (entropy_size == 0) ? 8u : entropy_size; - std::fill(std::begin(buffer) + requested_entropy, std::end(buffer), 0); - seq.generate(std::begin(buffer), std::begin(buffer) + requested_entropy); + std::fill(buffer + requested_entropy, buffer + kBufferSize, 0); + seq.generate(buffer, buffer + requested_entropy); +#ifdef ABSL_IS_BIG_ENDIAN + // Randen expects the seed buffer to be in Little Endian; reverse it on + // Big Endian platforms. + for (sequence_result_type& e : buffer) { + e = absl::little_endian::FromHost(e); + } +#endif // The Randen paper suggests preferentially initializing even-numbered // 128-bit vectors of the randen state (there are 16 such vectors). // The seed data is merged into the state offset by 128-bits, which - // implies prefering seed bytes [16..31, ..., 208..223]. Since the + // implies preferring seed bytes [16..31, ..., 208..223]. Since the // buffer is 32-bit values, we swap the corresponding buffer positions in // 128-bit chunks. size_t dst = kBufferSize; @@ -138,9 +157,9 @@ class alignas(16) randen_engine { std::swap(buffer[--dst], buffer[--src]); } } else { - seq.generate(std::begin(buffer), std::end(buffer)); + seq.generate(buffer, buffer + kBufferSize); } - impl_.Absorb(buffer, state_); + impl_.Absorb(buffer, state()); // Generate will be called when operator() is called next_ = kStateSizeT; @@ -151,9 +170,10 @@ class alignas(16) randen_engine { count -= step; constexpr uint64_t kRateT = kStateSizeT - kCapacityT; + auto* begin = state(); while (count > 0) { next_ = kCapacityT; - impl_.Generate(state_); + impl_.Generate(*reinterpret_cast(begin)); step = std::min(kRateT, count); count -= step; } @@ -161,9 +181,9 @@ class alignas(16) randen_engine { } bool operator==(const randen_engine& other) const { + const auto* begin = state(); return next_ == other.next_ && - std::equal(std::begin(state_), std::end(state_), - std::begin(other.state_)); + std::equal(begin, begin + kStateSizeT, other.state()); } bool operator!=(const randen_engine& other) const { @@ -177,11 +197,13 @@ class alignas(16) randen_engine { using numeric_type = typename random_internal::stream_format_type::type; auto saver = random_internal::make_ostream_state_saver(os); - for (const auto& elem : engine.state_) { + auto* it = engine.state(); + for (auto* end = it + kStateSizeT; it < end; ++it) { // In the case that `elem` is `uint8_t`, it must be cast to something // larger so that it prints as an integer rather than a character. For // simplicity, apply the cast all circumstances. - os << static_cast(elem) << os.fill(); + os << static_cast(little_endian::FromHost(*it)) + << os.fill(); } os << engine.next_; return os; @@ -200,13 +222,13 @@ class alignas(16) randen_engine { // necessary to read a wider type and then cast it to uint8_t. numeric_type value; is >> value; - elem = static_cast(value); + elem = little_endian::ToHost(static_cast(value)); } is >> next; if (is.fail()) { return is; } - std::memcpy(engine.state_, state, sizeof(engine.state_)); + std::memcpy(engine.state(), state, sizeof(state)); engine.next_ = next; return is; } @@ -217,9 +239,21 @@ class alignas(16) randen_engine { static constexpr size_t kCapacityT = Randen::kCapacityBytes / sizeof(result_type); - // First kCapacityT are `inner', the others are accessible random bits. - alignas(16) result_type state_[kStateSizeT]; - size_t next_; // index within state_ + // Returns the state array pointer, which is aligned to 16 bytes. + // The first kCapacityT are the `inner' sponge; the remainder are available. + result_type* state() { + return reinterpret_cast( + (reinterpret_cast(&raw_state_) & 0xf) ? (raw_state_ + 8) + : raw_state_); + } + const result_type* state() const { + return const_cast(this)->state(); + } + + // raw state array, manually aligned in state(). This overallocates + // by 8 bytes since C++ does not guarantee extended heap alignment. + alignas(8) char raw_state_[Randen::kStateBytes + 8]; + size_t next_; // index within state() Randen impl_; }; diff --git a/Siv3D/include/ThirdParty/absl/random/internal/randen_hwaes.h b/Siv3D/include/ThirdParty/absl/random/internal/randen_hwaes.h index bce36b5226..71a7f69f25 100644 --- a/Siv3D/include/ThirdParty/absl/random/internal/randen_hwaes.h +++ b/Siv3D/include/ThirdParty/absl/random/internal/randen_hwaes.h @@ -26,7 +26,7 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace random_internal { -// RANDen = RANDom generator or beetroots in Swiss German. +// RANDen = RANDom generator or beetroots in Swiss High German. // 'Strong' (well-distributed, unpredictable, backtracking-resistant) random // generator, faster in some benchmarks than std::mt19937_64 and pcg64_c32. // diff --git a/Siv3D/include/ThirdParty/absl/random/internal/randen_slow.h b/Siv3D/include/ThirdParty/absl/random/internal/randen_slow.h index b6f137eb94..532c3a8991 100644 --- a/Siv3D/include/ThirdParty/absl/random/internal/randen_slow.h +++ b/Siv3D/include/ThirdParty/absl/random/internal/randen_slow.h @@ -23,7 +23,7 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace random_internal { -// RANDen = RANDom generator or beetroots in Swiss German. +// RANDen = RANDom generator or beetroots in Swiss High German. // RandenSlow implements the basic state manipulation methods for // architectures lacking AES hardware acceleration intrinsics. class RandenSlow { diff --git a/Siv3D/include/ThirdParty/absl/random/internal/randen_traits.h b/Siv3D/include/ThirdParty/absl/random/internal/randen_traits.h index 53caa93614..120022c9fb 100644 --- a/Siv3D/include/ThirdParty/absl/random/internal/randen_traits.h +++ b/Siv3D/include/ThirdParty/absl/random/internal/randen_traits.h @@ -28,7 +28,7 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace random_internal { -// RANDen = RANDom generator or beetroots in Swiss German. +// RANDen = RANDom generator or beetroots in Swiss High German. // 'Strong' (well-distributed, unpredictable, backtracking-resistant) random // generator, faster in some benchmarks than std::mt19937_64 and pcg64_c32. // diff --git a/Siv3D/include/ThirdParty/absl/random/internal/salted_seed_seq.h b/Siv3D/include/ThirdParty/absl/random/internal/salted_seed_seq.h index 5953a090f8..06291865e3 100644 --- a/Siv3D/include/ThirdParty/absl/random/internal/salted_seed_seq.h +++ b/Siv3D/include/ThirdParty/absl/random/internal/salted_seed_seq.h @@ -22,6 +22,7 @@ #include #include #include +#include #include "absl/container/inlined_vector.h" #include "absl/meta/type_traits.h" @@ -65,15 +66,19 @@ class SaltedSeedSeq { template void generate(RandomAccessIterator begin, RandomAccessIterator end) { + using U = typename std::iterator_traits::value_type; + // The common case is that generate is called with ContiguousIterators // to uint arrays. Such contiguous memory regions may be optimized, // which we detect here. - using tag = absl::conditional_t< - (std::is_pointer::value && - std::is_same, uint32_t>::value), + using TagType = absl::conditional_t< + (std::is_same::value && + (std::is_pointer::value || + std::is_same::iterator>::value)), ContiguousAndUint32Tag, DefaultTag>; if (begin != end) { - generate_impl(begin, end, tag{}); + generate_impl(TagType{}, begin, end, std::distance(begin, end)); } } @@ -89,8 +94,15 @@ class SaltedSeedSeq { struct DefaultTag {}; // Generate which requires the iterators are contiguous pointers to uint32_t. - void generate_impl(uint32_t* begin, uint32_t* end, ContiguousAndUint32Tag) { - generate_contiguous(absl::MakeSpan(begin, end)); + // Fills the initial seed buffer the underlying SSeq::generate() call, + // then mixes in the salt material. + template + void generate_impl(ContiguousAndUint32Tag, Contiguous begin, Contiguous end, + size_t n) { + seq_->generate(begin, end); + const uint32_t salt = absl::random_internal::GetSaltMaterial().value_or(0); + auto span = absl::Span(&*begin, n); + MixIntoSeedMaterial(absl::MakeConstSpan(&salt, 1), span); } // The uncommon case for generate is that it is called with iterators over @@ -98,27 +110,13 @@ class SaltedSeedSeq { // case we allocate a temporary 32-bit buffer and then copy-assign back // to the initial inputs. template - void generate_impl(RandomAccessIterator begin, RandomAccessIterator end, - DefaultTag) { - return generate_and_copy(std::distance(begin, end), begin); - } - - // Fills the initial seed buffer the underlying SSeq::generate() call, - // mixing in the salt material. - void generate_contiguous(absl::Span buffer) { - seq_->generate(buffer.begin(), buffer.end()); - const uint32_t salt = absl::random_internal::GetSaltMaterial().value_or(0); - MixIntoSeedMaterial(absl::MakeConstSpan(&salt, 1), buffer); - } - - // Allocates a seed buffer of `n` elements, generates the seed, then - // copies the result into the `out` iterator. - template - void generate_and_copy(size_t n, Iterator out) { - // Allocate a temporary buffer, generate, and then copy. + void generate_impl(DefaultTag, RandomAccessIterator begin, + RandomAccessIterator, size_t n) { + // Allocates a seed buffer of `n` elements, generates the seed, then + // copies the result into the `out` iterator. absl::InlinedVector data(n, 0); - generate_contiguous(absl::MakeSpan(data.data(), data.size())); - std::copy(data.begin(), data.end(), out); + generate_impl(ContiguousAndUint32Tag{}, data.begin(), data.end(), n); + std::copy(data.begin(), data.end(), begin); } // Because [rand.req.seedseq] is not required to be copy-constructible, diff --git a/Siv3D/include/ThirdParty/absl/random/internal/traits.h b/Siv3D/include/ThirdParty/absl/random/internal/traits.h index 75772bd9ab..f874a0f78b 100644 --- a/Siv3D/include/ThirdParty/absl/random/internal/traits.h +++ b/Siv3D/include/ThirdParty/absl/random/internal/traits.h @@ -20,6 +20,8 @@ #include #include "absl/base/config.h" +#include "absl/numeric/bits.h" +#include "absl/numeric/int128.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -59,6 +61,31 @@ class is_widening_convertible { rank() <= rank(); }; +template +struct IsIntegral : std::is_integral {}; +template <> +struct IsIntegral : std::true_type {}; +template <> +struct IsIntegral : std::true_type {}; + +template +struct MakeUnsigned : std::make_unsigned {}; +template <> +struct MakeUnsigned { + using type = absl::uint128; +}; +template <> +struct MakeUnsigned { + using type = absl::uint128; +}; + +template +struct IsUnsigned : std::is_unsigned {}; +template <> +struct IsUnsigned : std::false_type {}; +template <> +struct IsUnsigned : std::true_type {}; + // unsigned_bits::type returns the unsigned int type with the indicated // number of bits. template @@ -81,19 +108,40 @@ struct unsigned_bits<64> { using type = uint64_t; }; -#ifdef ABSL_HAVE_INTRINSIC_INT128 template <> struct unsigned_bits<128> { - using type = __uint128_t; + using type = absl::uint128; +}; + +// 256-bit wrapper for wide multiplications. +struct U256 { + uint128 hi; + uint128 lo; +}; +template <> +struct unsigned_bits<256> { + using type = U256; }; -#endif template struct make_unsigned_bits { - using type = typename unsigned_bits::type>::digits>::type; + using type = typename unsigned_bits< + std::numeric_limits::type>::digits>::type; }; +template +int BitWidth(T v) { + // Workaround for bit_width not supporting int128. + // Don't hardcode `64` to make sure this code does not trigger compiler + // warnings in smaller types. + constexpr int half_bits = sizeof(T) * 8 / 2; + if (sizeof(T) == 16 && (v >> half_bits) != 0) { + return bit_width(static_cast(v >> half_bits)) + half_bits; + } else { + return bit_width(static_cast(v)); + } +} + } // namespace random_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/Siv3D/include/ThirdParty/absl/random/internal/uniform_helper.h b/Siv3D/include/ThirdParty/absl/random/internal/uniform_helper.h index 1243bc1c62..db737e13f3 100644 --- a/Siv3D/include/ThirdParty/absl/random/internal/uniform_helper.h +++ b/Siv3D/include/ThirdParty/absl/random/internal/uniform_helper.h @@ -100,7 +100,7 @@ using uniform_inferred_return_t = template typename absl::enable_if_t< absl::conjunction< - std::is_integral, + IsIntegral, absl::disjunction, std::is_same>>::value, IntType> @@ -131,7 +131,7 @@ uniform_lower_bound(Tag, NumType a, NumType) { template typename absl::enable_if_t< absl::conjunction< - std::is_integral, + IsIntegral, absl::disjunction, std::is_same>>::value, IntType> @@ -153,7 +153,7 @@ uniform_upper_bound(Tag, FloatType, FloatType b) { template typename absl::enable_if_t< absl::conjunction< - std::is_integral, + IsIntegral, absl::disjunction, std::is_same>>::value, IntType> @@ -201,7 +201,7 @@ is_uniform_range_valid(FloatType a, FloatType b) { } template -absl::enable_if_t::value, bool> +absl::enable_if_t::value, bool> is_uniform_range_valid(IntType a, IntType b) { return a <= b; } @@ -210,14 +210,14 @@ is_uniform_range_valid(IntType a, IntType b) { // or absl::uniform_real_distribution depending on the NumType parameter. template using UniformDistribution = - typename std::conditional::value, + typename std::conditional::value, absl::uniform_int_distribution, absl::uniform_real_distribution>::type; // UniformDistributionWrapper is used as the underlying distribution type // by the absl::Uniform template function. It selects the proper Abseil // uniform distribution and provides constructor overloads that match the -// expected parameter order as well as adjusting distribtuion bounds based +// expected parameter order as well as adjusting distribution bounds based // on the tag. template struct UniformDistributionWrapper : public UniformDistribution { diff --git a/Siv3D/include/ThirdParty/absl/random/internal/wide_multiply.h b/Siv3D/include/ThirdParty/absl/random/internal/wide_multiply.h index 0afcbe08e2..891e3630b7 100644 --- a/Siv3D/include/ThirdParty/absl/random/internal/wide_multiply.h +++ b/Siv3D/include/ThirdParty/absl/random/internal/wide_multiply.h @@ -26,7 +26,7 @@ #endif #include "absl/base/config.h" -#include "absl/base/internal/bits.h" +#include "absl/numeric/bits.h" #include "absl/numeric/int128.h" #include "absl/random/internal/traits.h" @@ -34,43 +34,6 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace random_internal { -// Helper object to multiply two 64-bit values to a 128-bit value. -// MultiplyU64ToU128 multiplies two 64-bit values to a 128-bit value. -// If an intrinsic is available, it is used, otherwise use native 32-bit -// multiplies to construct the result. -inline absl::uint128 MultiplyU64ToU128(uint64_t a, uint64_t b) { -#if defined(ABSL_HAVE_INTRINSIC_INT128) - return absl::uint128(static_cast<__uint128_t>(a) * b); -#elif defined(ABSL_INTERNAL_USE_UMUL128) - // uint64_t * uint64_t => uint128 multiply using imul intrinsic on MSVC. - uint64_t high = 0; - const uint64_t low = _umul128(a, b, &high); - return absl::MakeUint128(high, low); -#else - // uint128(a) * uint128(b) in emulated mode computes a full 128-bit x 128-bit - // multiply. However there are many cases where that is not necessary, and it - // is only necessary to support a 64-bit x 64-bit = 128-bit multiply. This is - // for those cases. - const uint64_t a00 = static_cast(a); - const uint64_t a32 = a >> 32; - const uint64_t b00 = static_cast(b); - const uint64_t b32 = b >> 32; - - const uint64_t c00 = a00 * b00; - const uint64_t c32a = a00 * b32; - const uint64_t c32b = a32 * b00; - const uint64_t c64 = a32 * b32; - - const uint32_t carry = - static_cast(((c00 >> 32) + static_cast(c32a) + - static_cast(c32b)) >> - 32); - - return absl::MakeUint128(c64 + (c32a >> 32) + (c32b >> 32) + carry, - c00 + (c32a << 32) + (c32b << 32)); -#endif -} - // wide_multiply multiplies two N-bit values to a 2N-bit result. template struct wide_multiply { @@ -82,27 +45,49 @@ struct wide_multiply { return static_cast(a) * b; } - static input_type hi(result_type r) { return r >> kN; } - static input_type lo(result_type r) { return r; } + static input_type hi(result_type r) { + return static_cast(r >> kN); + } + static input_type lo(result_type r) { return static_cast(r); } static_assert(std::is_unsigned::value, "Class-template wide_multiply<> argument must be unsigned."); }; -#ifndef ABSL_HAVE_INTRINSIC_INT128 +// MultiplyU128ToU256 multiplies two 128-bit values to a 256-bit value. +inline U256 MultiplyU128ToU256(uint128 a, uint128 b) { + const uint128 a00 = static_cast(a); + const uint128 a64 = a >> 64; + const uint128 b00 = static_cast(b); + const uint128 b64 = b >> 64; + + const uint128 c00 = a00 * b00; + const uint128 c64a = a00 * b64; + const uint128 c64b = a64 * b00; + const uint128 c128 = a64 * b64; + + const uint64_t carry = + static_cast(((c00 >> 64) + static_cast(c64a) + + static_cast(c64b)) >> + 64); + + return {c128 + (c64a >> 64) + (c64b >> 64) + carry, + c00 + (c64a << 64) + (c64b << 64)}; +} + + template <> -struct wide_multiply { - using input_type = uint64_t; - using result_type = absl::uint128; +struct wide_multiply { + using input_type = uint128; + using result_type = U256; - static result_type multiply(uint64_t a, uint64_t b) { - return MultiplyU64ToU128(a, b); + static result_type multiply(input_type a, input_type b) { + return MultiplyU128ToU256(a, b); } - static uint64_t hi(result_type r) { return absl::Uint128High64(r); } - static uint64_t lo(result_type r) { return absl::Uint128Low64(r); } + static input_type hi(result_type r) { return r.hi; } + static input_type lo(result_type r) { return r.lo; } }; -#endif } // namespace random_internal ABSL_NAMESPACE_END diff --git a/Siv3D/include/ThirdParty/absl/random/log_uniform_int_distribution.h b/Siv3D/include/ThirdParty/absl/random/log_uniform_int_distribution.h index 960816e2f8..4afff8f604 100644 --- a/Siv3D/include/ThirdParty/absl/random/log_uniform_int_distribution.h +++ b/Siv3D/include/ThirdParty/absl/random/log_uniform_int_distribution.h @@ -23,6 +23,7 @@ #include #include +#include "absl/numeric/bits.h" #include "absl/random/internal/fastmath.h" #include "absl/random/internal/generate_real.h" #include "absl/random/internal/iostream_state_saver.h" @@ -68,7 +69,7 @@ class log_uniform_int_distribution { if (base_ == 2) { // Determine where the first set bit is on range(), giving a log2(range) // value which can be used to construct bounds. - log_range_ = (std::min)(random_internal::LeadingSetBit(range()), + log_range_ = (std::min)(random_internal::BitWidth(range()), std::numeric_limits::digits); } else { // NOTE: Computing the logN(x) introduces error from 2 sources: @@ -80,7 +81,7 @@ class log_uniform_int_distribution { // // Thus a result which should equal K may equal K +/- epsilon, // which can eliminate some values depending on where the bounds fall. - const double inv_log_base = 1.0 / std::log(base_); + const double inv_log_base = 1.0 / std::log(static_cast(base_)); const double log_range = std::log(static_cast(range()) + 0.5); log_range_ = static_cast(std::ceil(inv_log_base * log_range)); } @@ -110,7 +111,7 @@ class log_uniform_int_distribution { unsigned_type range_; // max - min int log_range_; // ceil(logN(range_)) - static_assert(std::is_integral::value, + static_assert(random_internal::IsIntegral::value, "Class-template absl::log_uniform_int_distribution<> must be " "parameterized using an integral type."); }; @@ -136,7 +137,7 @@ class log_uniform_int_distribution { template result_type operator()(URBG& g, // NOLINT(runtime/references) const param_type& p) { - return (p.min)() + Generate(g, p); + return static_cast((p.min)() + Generate(g, p)); } result_type(min)() const { return (param_.min)(); } @@ -190,8 +191,8 @@ log_uniform_int_distribution::Generate( ? (std::numeric_limits::max)() : (static_cast(1) << e) - 1; } else { - const double r = std::pow(p.base(), d); - const double s = (r * p.base()) - 1.0; + const double r = std::pow(static_cast(p.base()), d); + const double s = (r * static_cast(p.base())) - 1.0; base_e = (r > static_cast((std::numeric_limits::max)())) @@ -208,7 +209,8 @@ log_uniform_int_distribution::Generate( const unsigned_type hi = (top_e >= p.range()) ? p.range() : top_e; // choose uniformly over [lo, hi] - return absl::uniform_int_distribution(lo, hi)(g); + return absl::uniform_int_distribution( + static_cast(lo), static_cast(hi))(g); } template diff --git a/Siv3D/include/ThirdParty/absl/random/mocking_bit_gen.h b/Siv3D/include/ThirdParty/absl/random/mocking_bit_gen.h index 6d2f2c8362..89fa5a47a6 100644 --- a/Siv3D/include/ThirdParty/absl/random/mocking_bit_gen.h +++ b/Siv3D/include/ThirdParty/absl/random/mocking_bit_gen.h @@ -87,7 +87,7 @@ class BitGenRef; // // ON_CALL(absl::MockUniform(), Call(bitgen, testing::_, testing::_)) // .WillByDefault([] (int low, int high) { -// return (low + high) / 2; +// return low + (high - low) / 2; // }); // // EXPECT_EQ(absl::Uniform(gen, 0, 10), 5); @@ -104,10 +104,7 @@ class BitGenRef; class MockingBitGen { public: MockingBitGen() = default; - - ~MockingBitGen() { - for (const auto& del : deleters_) del(); - } + ~MockingBitGen() = default; // URBG interface using result_type = absl::BitGen::result_type; @@ -117,14 +114,6 @@ class MockingBitGen { result_type operator()() { return gen_(); } private: - using match_impl_fn = void (*)(void* mock_fn, void* t_erased_arg_tuple, - void* t_erased_result); - - struct MockData { - void* mock_fn = nullptr; - match_impl_fn match_impl = nullptr; - }; - // GetMockFnType returns the testing::MockFunction for a result and tuple. // This method only exists for type deduction and is otherwise unimplemented. template @@ -136,17 +125,46 @@ class MockingBitGen { // NOTE: MockFnCaller is essentially equivalent to the lambda: // [fn](auto... args) { return fn->Call(std::move(args)...)} // however that fails to build on some supported platforms. - template + template struct MockFnCaller; + // specialization for std::tuple. - template - struct MockFnCaller> { + template + struct MockFnCaller> { MockFnType* fn; inline ResultT operator()(Args... args) { return fn->Call(std::move(args)...); } }; + // FunctionHolder owns a particular ::testing::MockFunction associated with + // a mocked type signature, and implement the type-erased Apply call, which + // applies type-erased arguments to the mock. + class FunctionHolder { + public: + virtual ~FunctionHolder() = default; + + // Call is a dispatch function which converts the + // generic type-erased parameters into a specific mock invocation call. + virtual void Apply(/*ArgTupleT*/ void* args_tuple, + /*ResultT*/ void* result) = 0; + }; + + template + class FunctionHolderImpl final : public FunctionHolder { + public: + void Apply(void* args_tuple, void* result) override { + // Requires tuple_args to point to a ArgTupleT, which is a + // std::tuple used to invoke the mock function. Requires result + // to point to a ResultT, which is the result of the call. + *static_cast(result) = + absl::apply(MockFnCaller{&mock_fn_}, + *static_cast(args_tuple)); + } + + MockFnType mock_fn_; + }; + // MockingBitGen::RegisterMock // // RegisterMock(FastTypeIdType) is the main extension @@ -157,37 +175,31 @@ class MockingBitGen { // // The returned MockFunction<...> type can be used to setup additional // distribution parameters of the expectation. - template - auto RegisterMock(base_internal::FastTypeIdType type) + template + auto RegisterMock(SelfT&, base_internal::FastTypeIdType type) -> decltype(GetMockFnType(std::declval(), std::declval()))& { - using MockFnType = decltype( - GetMockFnType(std::declval(), std::declval())); + using MockFnType = decltype(GetMockFnType(std::declval(), + std::declval())); + + using WrappedFnType = absl::conditional_t< + std::is_same>::value, + ::testing::NiceMock, + absl::conditional_t< + std::is_same>::value, + ::testing::NaggyMock, + absl::conditional_t< + std::is_same>::value, + ::testing::StrictMock, MockFnType>>>; + + using ImplT = FunctionHolderImpl; auto& mock = mocks_[type]; - if (!mock.mock_fn) { - auto* mock_fn = new MockFnType; - mock.mock_fn = mock_fn; - mock.match_impl = &MatchImpl; - deleters_.emplace_back([mock_fn] { delete mock_fn; }); + if (!mock) { + mock = absl::make_unique(); } - return *static_cast(mock.mock_fn); - } - - // MockingBitGen::MatchImpl<> is a dispatch function which converts the - // generic type-erased parameters into a specific mock invocation call. - // Requires tuple_args to point to a ArgTupleT, which is a std::tuple - // used to invoke the mock function. - // Requires result to point to a ResultT, which is the result of the call. - template - static void MatchImpl(/*MockFnType*/ void* mock_fn, - /*ArgTupleT*/ void* args_tuple, - /*ResultT*/ void* result) { - using MockFnType = decltype( - GetMockFnType(std::declval(), std::declval())); - *static_cast(result) = absl::apply( - MockFnCaller{ - static_cast(mock_fn)}, - *static_cast(args_tuple)); + return static_cast(mock.get())->mock_fn_; } // MockingBitGen::InvokeMock @@ -206,13 +218,13 @@ class MockingBitGen { // Trigger a mock, if there exists one that matches `param`. auto it = mocks_.find(type); if (it == mocks_.end()) return false; - auto* mock_data = static_cast(&it->second); - mock_data->match_impl(mock_data->mock_fn, args_tuple, result); + it->second->Apply(args_tuple, result); return true; } - absl::flat_hash_map mocks_; - std::vector> deleters_; + absl::flat_hash_map> + mocks_; absl::BitGen gen_; template diff --git a/Siv3D/include/ThirdParty/absl/random/poisson_distribution.h b/Siv3D/include/ThirdParty/absl/random/poisson_distribution.h index cb5f5d5d0f..f4573082e1 100644 --- a/Siv3D/include/ThirdParty/absl/random/poisson_distribution.h +++ b/Siv3D/include/ThirdParty/absl/random/poisson_distribution.h @@ -26,6 +26,7 @@ #include "absl/random/internal/fastmath.h" #include "absl/random/internal/generate_real.h" #include "absl/random/internal/iostream_state_saver.h" +#include "absl/random/internal/traits.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -80,7 +81,7 @@ class poisson_distribution { double log_k_; int split_; - static_assert(std::is_integral::value, + static_assert(random_internal::IsIntegral::value, "Class-template absl::poisson_distribution<> must be " "parameterized using an integral type."); }; @@ -133,7 +134,8 @@ template poisson_distribution::param_type::param_type(double mean) : mean_(mean), split_(0) { assert(mean >= 0); - assert(mean <= (std::numeric_limits::max)()); + assert(mean <= + static_cast((std::numeric_limits::max)())); // As a defensive measure, avoid large values of the mean. The rejection // algorithm used does not support very large values well. It my be worth // changing algorithms to better deal with these cases. @@ -222,8 +224,9 @@ poisson_distribution::operator()( // clang-format on const double lhs = 2.0 * std::log(u) + p.log_k_ + s; if (lhs < rhs) { - return x > (max)() ? (max)() - : static_cast(x); // f(x)/k >= u^2 + return x > static_cast((max)()) + ? (max)() + : static_cast(x); // f(x)/k >= u^2 } } } diff --git a/Siv3D/include/ThirdParty/absl/random/random.h b/Siv3D/include/ThirdParty/absl/random/random.h index 71b6309288..767208671c 100644 --- a/Siv3D/include/ThirdParty/absl/random/random.h +++ b/Siv3D/include/ThirdParty/absl/random/random.h @@ -68,7 +68,7 @@ ABSL_NAMESPACE_BEGIN // // `absl::BitGen` may be constructed with an optional seed sequence type, // conforming to [rand.req.seed_seq], which will be mixed with additional -// non-deterministic data. +// non-deterministic data as detailed below. // // Example: // @@ -79,16 +79,16 @@ ABSL_NAMESPACE_BEGIN // // Generate an integer value in the closed interval [1,6] // int die_roll2 = absl::uniform_int_distribution(1, 6)(gen_with_seed); // +// Constructing two `absl::BitGen`s with the same seed sequence in the same +// process will produce the same sequence of variates, but need not do so across +// multiple processes even if they're executing the same binary. +// // `absl::BitGen` meets the requirements of the Uniform Random Bit Generator // (URBG) concept as per the C++17 standard [rand.req.urng] though differs // slightly with [rand.req.eng]. Like its standard library equivalents (e.g. // `std::mersenne_twister_engine`) `absl::BitGen` is not cryptographically // secure. // -// Constructing two `absl::BitGen`s with the same seed sequence in the same -// binary will produce the same sequence of variates within the same binary, but -// need not do so across multiple binary invocations. -// // This type has been optimized to perform better than Mersenne Twister // (https://en.wikipedia.org/wiki/Mersenne_Twister) and many other complex URBG // types on modern x86, ARM, and PPC architectures. @@ -147,7 +147,7 @@ using BitGen = random_internal::NonsecureURBGBase< // // `absl::InsecureBitGen` may be constructed with an optional seed sequence // type, conforming to [rand.req.seed_seq], which will be mixed with additional -// non-deterministic data. (See std_seed_seq.h for more information.) +// non-deterministic data, as detailed in the `absl::BitGen` comment. // // `absl::InsecureBitGen` meets the requirements of the Uniform Random Bit // Generator (URBG) concept as per the C++17 standard [rand.req.urng] though diff --git a/Siv3D/include/ThirdParty/absl/random/seed_sequences.h b/Siv3D/include/ThirdParty/absl/random/seed_sequences.h index ff1340cc8e..c3af4b00a4 100644 --- a/Siv3D/include/ThirdParty/absl/random/seed_sequences.h +++ b/Siv3D/include/ThirdParty/absl/random/seed_sequences.h @@ -28,6 +28,7 @@ #include #include +#include "absl/base/config.h" #include "absl/random/internal/salted_seed_seq.h" #include "absl/random/internal/seed_material.h" #include "absl/random/seed_gen_exception.h" diff --git a/Siv3D/include/ThirdParty/absl/random/uniform_int_distribution.h b/Siv3D/include/ThirdParty/absl/random/uniform_int_distribution.h index da66564a6b..fae80252e3 100644 --- a/Siv3D/include/ThirdParty/absl/random/uniform_int_distribution.h +++ b/Siv3D/include/ThirdParty/absl/random/uniform_int_distribution.h @@ -97,7 +97,7 @@ class uniform_int_distribution { result_type lo_; unsigned_type range_; - static_assert(std::is_integral::value, + static_assert(random_internal::IsIntegral::value, "Class-template absl::uniform_int_distribution<> must be " "parameterized using an integral type."); }; // param_type @@ -125,7 +125,7 @@ class uniform_int_distribution { template result_type operator()( URBG& gen, const param_type& param) { // NOLINT(runtime/references) - return param.a() + Generate(gen, param.range()); + return static_cast(param.a() + Generate(gen, param.range())); } result_type a() const { return param_.a(); } @@ -196,7 +196,7 @@ typename random_internal::make_unsigned_bits::type uniform_int_distribution::Generate( URBG& g, // NOLINT(runtime/references) typename random_internal::make_unsigned_bits::type R) { - random_internal::FastUniformBits fast_bits; + random_internal::FastUniformBits fast_bits; unsigned_type bits = fast_bits(g); const unsigned_type Lim = R + 1; if ((R & Lim) == 0) { diff --git a/Siv3D/include/ThirdParty/absl/random/uniform_real_distribution.h b/Siv3D/include/ThirdParty/absl/random/uniform_real_distribution.h index 5ba17b2341..196833415e 100644 --- a/Siv3D/include/ThirdParty/absl/random/uniform_real_distribution.h +++ b/Siv3D/include/ThirdParty/absl/random/uniform_real_distribution.h @@ -73,12 +73,12 @@ class uniform_real_distribution { : lo_(lo), hi_(hi), range_(hi - lo) { // [rand.dist.uni.real] preconditions 2 & 3 assert(lo <= hi); + // NOTE: For integral types, we can promote the range to an unsigned type, // which gives full width of the range. However for real (fp) types, this // is not possible, so value generation cannot use the full range of the // real type. assert(range_ <= (std::numeric_limits::max)()); - assert(std::isfinite(range_)); } result_type a() const { return lo_; } diff --git a/Siv3D/include/ThirdParty/absl/random/zipf_distribution.h b/Siv3D/include/ThirdParty/absl/random/zipf_distribution.h index 22ebc756cf..03497b1b26 100644 --- a/Siv3D/include/ThirdParty/absl/random/zipf_distribution.h +++ b/Siv3D/include/ThirdParty/absl/random/zipf_distribution.h @@ -23,13 +23,14 @@ #include #include "absl/random/internal/iostream_state_saver.h" +#include "absl/random/internal/traits.h" #include "absl/random/uniform_real_distribution.h" namespace absl { ABSL_NAMESPACE_BEGIN // absl::zipf_distribution produces random integer-values in the range [0, k], -// distributed according to the discrete probability function: +// distributed according to the unnormalized discrete probability function: // // P(x) = (v + x) ^ -q // @@ -94,7 +95,7 @@ class zipf_distribution { double hxm_; // h(k + 0.5) double hx0_minus_hxm_; // h(x0) - h(k + 0.5) - static_assert(std::is_integral::value, + static_assert(random_internal::IsIntegral::value, "Class-template absl::zipf_distribution<> must be " "parameterized using an integral type."); }; @@ -221,7 +222,7 @@ zipf_distribution::operator()( const double u = p.hxm_ + v * p.hx0_minus_hxm_; const double x = p.hinv(u); k = rint(x); // std::floor(x + 0.5); - if (k > p.k()) continue; // reject k > max_k + if (k > static_cast(p.k())) continue; // reject k > max_k if (k - x <= p.s_) break; const double h = p.h(k + 0.5); const double r = p.pow_negative_q(p.v_ + k); diff --git a/Siv3D/include/ThirdParty/absl/strings/ascii.h b/Siv3D/include/ThirdParty/absl/strings/ascii.h index b46bc71f35..42eadaea6c 100644 --- a/Siv3D/include/ThirdParty/absl/strings/ascii.h +++ b/Siv3D/include/ThirdParty/absl/strings/ascii.h @@ -133,7 +133,7 @@ inline bool ascii_isdigit(unsigned char c) { return c >= '0' && c <= '9'; } // ascii_isprint() // -// Determines whether the given character is printable, including whitespace. +// Determines whether the given character is printable, including spaces. inline bool ascii_isprint(unsigned char c) { return c >= 32 && c < 127; } // ascii_isgraph() @@ -197,7 +197,7 @@ ABSL_MUST_USE_RESULT inline std::string AsciiStrToUpper(absl::string_view s) { ABSL_MUST_USE_RESULT inline absl::string_view StripLeadingAsciiWhitespace( absl::string_view str) { auto it = std::find_if_not(str.begin(), str.end(), absl::ascii_isspace); - return str.substr(it - str.begin()); + return str.substr(static_cast(it - str.begin())); } // Strips in place whitespace from the beginning of the given string. @@ -211,13 +211,13 @@ inline void StripLeadingAsciiWhitespace(std::string* str) { ABSL_MUST_USE_RESULT inline absl::string_view StripTrailingAsciiWhitespace( absl::string_view str) { auto it = std::find_if_not(str.rbegin(), str.rend(), absl::ascii_isspace); - return str.substr(0, str.rend() - it); + return str.substr(0, static_cast(str.rend() - it)); } // Strips in place whitespace from the end of the given string inline void StripTrailingAsciiWhitespace(std::string* str) { auto it = std::find_if_not(str->rbegin(), str->rend(), absl::ascii_isspace); - str->erase(str->rend() - it); + str->erase(static_cast(str->rend() - it)); } // Returns absl::string_view with whitespace stripped from both ends of the diff --git a/Siv3D/include/ThirdParty/absl/strings/charconv.h b/Siv3D/include/ThirdParty/absl/strings/charconv.h index e04be32f95..111c7120de 100644 --- a/Siv3D/include/ThirdParty/absl/strings/charconv.h +++ b/Siv3D/include/ThirdParty/absl/strings/charconv.h @@ -22,7 +22,7 @@ namespace absl { ABSL_NAMESPACE_BEGIN -// Workalike compatibilty version of std::chars_format from C++17. +// Workalike compatibility version of std::chars_format from C++17. // // This is an bitfield enumerator which can be passed to absl::from_chars to // configure the string-to-float conversion. @@ -48,7 +48,7 @@ struct from_chars_result { std::errc ec; }; -// Workalike compatibilty version of std::from_chars from C++17. Currently +// Workalike compatibility version of std::from_chars from C++17. Currently // this only supports the `double` and `float` types. // // This interface incorporates the proposed resolutions for library issues @@ -64,8 +64,9 @@ struct from_chars_result { // the result in `value`. // // The matching pattern format is almost the same as that of strtod(), except -// that C locale is not respected, and an initial '+' character in the input -// range will never be matched. +// that (1) C locale is not respected, (2) an initial '+' character in the +// input range will never be matched, and (3) leading whitespaces are not +// ignored. // // If `fmt` is set, it must be one of the enumerator values of the chars_format. // (This is despite the fact that chars_format is a bitmask type.) If set to diff --git a/Siv3D/include/ThirdParty/absl/strings/cord.h b/Siv3D/include/ThirdParty/absl/strings/cord.h index b8b251b02b..457ccf0609 100644 --- a/Siv3D/include/ThirdParty/absl/strings/cord.h +++ b/Siv3D/include/ThirdParty/absl/strings/cord.h @@ -20,12 +20,11 @@ // structure. A Cord is a string-like sequence of characters optimized for // specific use cases. Unlike a `std::string`, which stores an array of // contiguous characters, Cord data is stored in a structure consisting of -// separate, reference-counted "chunks." (Currently, this implementation is a -// tree structure, though that implementation may change.) +// separate, reference-counted "chunks." // // Because a Cord consists of these chunks, data can be added to or removed from // a Cord during its lifetime. Chunks may also be shared between Cords. Unlike a -// `std::string`, a Cord can therefore accomodate data that changes over its +// `std::string`, a Cord can therefore accommodate data that changes over its // lifetime, though it's not quite "mutable"; it can change only in the // attachment, detachment, or rearrangement of chunks of its constituent data. // @@ -70,15 +69,31 @@ #include #include +#include "absl/base/attributes.h" +#include "absl/base/config.h" #include "absl/base/internal/endian.h" #include "absl/base/internal/per_thread_tls.h" #include "absl/base/macros.h" #include "absl/base/port.h" #include "absl/container/inlined_vector.h" +#include "absl/crc/internal/crc_cord_state.h" #include "absl/functional/function_ref.h" #include "absl/meta/type_traits.h" +#include "absl/strings/cord_analysis.h" +#include "absl/strings/cord_buffer.h" +#include "absl/strings/internal/cord_data_edge.h" #include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_btree.h" +#include "absl/strings/internal/cord_rep_btree_reader.h" +#include "absl/strings/internal/cord_rep_crc.h" +#include "absl/strings/internal/cord_rep_ring.h" +#include "absl/strings/internal/cordz_functions.h" +#include "absl/strings/internal/cordz_info.h" +#include "absl/strings/internal/cordz_statistics.h" +#include "absl/strings/internal/cordz_update_scope.h" +#include "absl/strings/internal/cordz_update_tracker.h" #include "absl/strings/internal/resize_uninitialized.h" +#include "absl/strings/internal/string_constant.h" #include "absl/strings/string_view.h" #include "absl/types/optional.h" @@ -90,6 +105,41 @@ template Cord MakeCordFromExternal(absl::string_view, Releaser&&); void CopyCordToString(const Cord& src, std::string* dst); +// Cord memory accounting modes +enum class CordMemoryAccounting { + // Counts the *approximate* number of bytes held in full or in part by this + // Cord (which may not remain the same between invocations). Cords that share + // memory could each be "charged" independently for the same shared memory. + // See also comment on `kTotalMorePrecise` on internally shared memory. + kTotal, + + // Counts the *approximate* number of bytes held in full or in part by this + // Cord for the distinct memory held by this cord. This option is similar + // to `kTotal`, except that if the cord has multiple references to the same + // memory, that memory is only counted once. + // + // For example: + // absl::Cord cord; + // cord.append(some_other_cord); + // cord.append(some_other_cord); + // // Counts `some_other_cord` twice: + // cord.EstimatedMemoryUsage(kTotal); + // // Counts `some_other_cord` once: + // cord.EstimatedMemoryUsage(kTotalMorePrecise); + // + // The `kTotalMorePrecise` number is more expensive to compute as it requires + // deduplicating all memory references. Applications should prefer to use + // `kFairShare` or `kTotal` unless they really need a more precise estimate + // on "how much memory is potentially held / kept alive by this cord?" + kTotalMorePrecise, + + // Counts the *approximate* number of bytes held in full or in part by this + // Cord weighted by the sharing ratio of that data. For example, if some data + // edge is shared by 4 different Cords, then each cord is attributed 1/4th of + // the total memory usage as a 'fair share' of the total memory usage. + kFairShare, +}; + // Cord // // A Cord is a sequence of characters, designed to be more efficient than a @@ -204,7 +254,7 @@ class Cord { // // Releases the Cord data. Any nodes that share data with other Cords, if // applicable, will have their reference counts reduced by 1. - void Clear(); + ABSL_ATTRIBUTE_REINITIALIZES void Clear(); // Cord::Append() // @@ -216,6 +266,58 @@ class Cord { template = 0> void Append(T&& src); + // Appends `buffer` to this cord, unless `buffer` has a zero length in which + // case this method has no effect on this cord instance. + // This method is guaranteed to consume `buffer`. + void Append(CordBuffer buffer); + + // Returns a CordBuffer, re-using potential existing capacity in this cord. + // + // Cord instances may have additional unused capacity in the last (or first) + // nodes of the underlying tree to facilitate amortized growth. This method + // allows applications to explicitly use this spare capacity if available, + // or create a new CordBuffer instance otherwise. + // If this cord has a final non-shared node with at least `min_capacity` + // available, then this method will return that buffer including its data + // contents. I.e.; the returned buffer will have a non-zero length, and + // a capacity of at least `buffer.length + min_capacity`. Otherwise, this + // method will return `CordBuffer::CreateWithDefaultLimit(capacity)`. + // + // Below an example of using GetAppendBuffer. Notice that in this example we + // use `GetAppendBuffer()` only on the first iteration. As we know nothing + // about any initial extra capacity in `cord`, we may be able to use the extra + // capacity. But as we add new buffers with fully utilized contents after that + // we avoid calling `GetAppendBuffer()` on subsequent iterations: while this + // works fine, it results in an unnecessary inspection of cord contents: + // + // void AppendRandomDataToCord(absl::Cord &cord, size_t n) { + // bool first = true; + // while (n > 0) { + // CordBuffer buffer = first ? cord.GetAppendBuffer(n) + // : CordBuffer::CreateWithDefaultLimit(n); + // absl::Span data = buffer.available_up_to(n); + // FillRandomValues(data.data(), data.size()); + // buffer.IncreaseLengthBy(data.size()); + // cord.Append(std::move(buffer)); + // n -= data.size(); + // first = false; + // } + // } + CordBuffer GetAppendBuffer(size_t capacity, size_t min_capacity = 16); + + // Returns a CordBuffer, re-using potential existing capacity in this cord. + // + // This function is identical to `GetAppendBuffer`, except that in the case + // where a new `CordBuffer` is allocated, it is allocated using the provided + // custom limit instead of the default limit. `GetAppendBuffer` will default + // to `CordBuffer::CreateWithDefaultLimit(capacity)` whereas this method + // will default to `CordBuffer::CreateWithCustomLimit(block_size, capacity)`. + // This method is equivalent to `GetAppendBuffer` if `block_size` is zero. + // See the documentation for `CreateWithCustomLimit` for more details on the + // restrictions and legal values for `block_size`. + CordBuffer GetCustomAppendBuffer(size_t block_size, size_t capacity, + size_t min_capacity = 16); + // Cord::Prepend() // // Prepends data to the Cord, which may come from another Cord or other string @@ -225,6 +327,11 @@ class Cord { template = 0> void Prepend(T&& src); + // Prepends `buffer` to this cord, unless `buffer` has a zero length in which + // case this method has no effect on this cord instance. + // This method is guaranteed to consume `buffer`. + void Prepend(CordBuffer buffer); + // Cord::RemovePrefix() // // Removes the first `n` bytes of a Cord. @@ -246,9 +353,7 @@ class Cord { // swap() // // Swaps the contents of two Cords. - friend void swap(Cord& x, Cord& y) noexcept { - x.swap(y); - } + friend void swap(Cord& x, Cord& y) noexcept { x.swap(y); } // Cord::size() // @@ -262,11 +367,10 @@ class Cord { // Cord::EstimatedMemoryUsage() // - // Returns the *approximate* number of bytes held in full or in part by this - // Cord (which may not remain the same between invocations). Note that Cords - // that share memory could each be "charged" independently for the same shared - // memory. - size_t EstimatedMemoryUsage() const; + // Returns the *approximate* number of bytes held by this cord. + // See CordMemoryAccounting for more information on the accounting method. + size_t EstimatedMemoryUsage(CordMemoryAccounting accounting_method = + CordMemoryAccounting::kTotal) const; // Cord::Compare() // @@ -286,7 +390,7 @@ class Cord { bool StartsWith(const Cord& rhs) const; bool StartsWith(absl::string_view rhs) const; - // Cord::EndsWidth() + // Cord::EndsWith() // // Determines whether the Cord ends with the passed string data `rhs`. bool EndsWith(absl::string_view rhs) const; @@ -316,7 +420,7 @@ class Cord { //---------------------------------------------------------------------------- // // A `Cord::ChunkIterator` allows iteration over the constituent chunks of its - // Cord. Such iteration allows you to perform non-const operatons on the data + // Cord. Such iteration allows you to perform non-const operations on the data // of a Cord without modifying it. // // Generally, you do not instantiate a `Cord::ChunkIterator` directly; @@ -360,17 +464,28 @@ class Cord { friend class CharIterator; private: + using CordRep = absl::cord_internal::CordRep; + using CordRepBtree = absl::cord_internal::CordRepBtree; + using CordRepBtreeReader = absl::cord_internal::CordRepBtreeReader; + + // Constructs a `begin()` iterator from `tree`. `tree` must not be null. + explicit ChunkIterator(cord_internal::CordRep* tree); + // Constructs a `begin()` iterator from `cord`. explicit ChunkIterator(const Cord* cord); + // Initializes this instance from a tree. Invoked by constructors. + void InitTree(cord_internal::CordRep* tree); + // Removes `n` bytes from `current_chunk_`. Expects `n` to be smaller than // `current_chunk_.size()`. void RemoveChunkPrefix(size_t n); Cord AdvanceAndReadBytes(size_t n); void AdvanceBytes(size_t n); - // Iterates `n` bytes, where `n` is expected to be greater than or equal to - // `current_chunk_.size()`. - void AdvanceBytesSlowPath(size_t n); + + // Btree specific operator++ + ChunkIterator& AdvanceBtree(); + void AdvanceBytesBtree(size_t n); // A view into bytes of the current `CordRep`. It may only be a view to a // suffix of bytes if this is being used by `CharIterator`. @@ -381,11 +496,12 @@ class Cord { absl::cord_internal::CordRep* current_leaf_ = nullptr; // The number of bytes left in the `Cord` over which we are iterating. size_t bytes_remaining_ = 0; - absl::InlinedVector - stack_of_right_children_; + + // Cord reader for cord btrees. Empty if not traversing a btree. + CordRepBtreeReader btree_reader_; }; - // Cord::ChunkIterator::chunk_begin() + // Cord::chunk_begin() // // Returns an iterator to the first chunk of the `Cord`. // @@ -401,7 +517,7 @@ class Cord { // } ChunkIterator chunk_begin() const; - // Cord::ChunkItertator::chunk_end() + // Cord::chunk_end() // // Returns an iterator one increment past the last chunk of the `Cord`. // @@ -411,7 +527,7 @@ class Cord { ChunkIterator chunk_end() const; //---------------------------------------------------------------------------- - // Cord::ChunkIterator::ChunkRange + // Cord::ChunkRange //---------------------------------------------------------------------------- // // `ChunkRange` is a helper class for iterating over the chunks of the `Cord`, @@ -424,6 +540,16 @@ class Cord { // `Cord::chunk_begin()` and `Cord::chunk_end()`. class ChunkRange { public: + // Fulfill minimum c++ container requirements [container.requirements] + // These (partial) container type definitions allow ChunkRange to be used + // in various utilities expecting a subset of [container.requirements]. + // For example, the below enables using `::testing::ElementsAre(...)` + using value_type = absl::string_view; + using reference = value_type&; + using const_reference = const value_type&; + using iterator = ChunkIterator; + using const_iterator = ChunkIterator; + explicit ChunkRange(const Cord* cord) : cord_(cord) {} ChunkIterator begin() const; @@ -435,9 +561,9 @@ class Cord { // Cord::Chunks() // - // Returns a `Cord::ChunkIterator::ChunkRange` for iterating over the chunks - // of a `Cord` with a range-based for-loop. For most iteration tasks on a - // Cord, use `Cord::Chunks()` to retrieve this iterator. + // Returns a `Cord::ChunkRange` for iterating over the chunks of a `Cord` with + // a range-based for-loop. For most iteration tasks on a Cord, use + // `Cord::Chunks()` to retrieve this iterator. // // Example: // @@ -503,7 +629,7 @@ class Cord { ChunkIterator chunk_iterator_; }; - // Cord::CharIterator::AdvanceAndRead() + // Cord::AdvanceAndRead() // // Advances the `Cord::CharIterator` by `n_bytes` and returns the bytes // advanced as a separate `Cord`. `n_bytes` must be less than or equal to the @@ -511,21 +637,21 @@ class Cord { // valid to pass `char_end()` and `0`. static Cord AdvanceAndRead(CharIterator* it, size_t n_bytes); - // Cord::CharIterator::Advance() + // Cord::Advance() // // Advances the `Cord::CharIterator` by `n_bytes`. `n_bytes` must be less than // or equal to the number of bytes remaining within the Cord; otherwise, // behavior is undefined. It is valid to pass `char_end()` and `0`. static void Advance(CharIterator* it, size_t n_bytes); - // Cord::CharIterator::ChunkRemaining() + // Cord::ChunkRemaining() // // Returns the longest contiguous view starting at the iterator's position. // // `it` must be dereferenceable. static absl::string_view ChunkRemaining(const CharIterator& it); - // Cord::CharIterator::char_begin() + // Cord::char_begin() // // Returns an iterator to the first character of the `Cord`. // @@ -534,7 +660,7 @@ class Cord { // a `CharIterator` where range-based for-loops may not be available. CharIterator char_begin() const; - // Cord::CharIterator::char_end() + // Cord::char_end() // // Returns an iterator to one past the last character of the `Cord`. // @@ -543,18 +669,28 @@ class Cord { // a `CharIterator` where range-based for-loops are not useful. CharIterator char_end() const; - // Cord::CharIterator::CharRange + // Cord::CharRange // // `CharRange` is a helper class for iterating over the characters of a // producing an iterator which can be used within a range-based for loop. // Construction of a `CharRange` will return an iterator pointing to the first // character of the Cord. Generally, do not construct a `CharRange` directly; - // instead, prefer to use the `Cord::Chars()` method show below. + // instead, prefer to use the `Cord::Chars()` method shown below. // // Implementation note: `CharRange` is simply a convenience wrapper over // `Cord::char_begin()` and `Cord::char_end()`. class CharRange { public: + // Fulfill minimum c++ container requirements [container.requirements] + // These (partial) container type definitions allow CharRange to be used + // in various utilities expecting a subset of [container.requirements]. + // For example, the below enables using `::testing::ElementsAre(...)` + using value_type = char; + using reference = value_type&; + using const_reference = const value_type&; + using iterator = CharIterator; + using const_iterator = CharIterator; + explicit CharRange(const Cord* cord) : cord_(cord) {} CharIterator begin() const; @@ -564,11 +700,11 @@ class Cord { const Cord* cord_; }; - // Cord::CharIterator::Chars() + // Cord::Chars() // - // Returns a `Cord::CharIterator` for iterating over the characters of a - // `Cord` with a range-based for-loop. For most character-based iteration - // tasks on a Cord, use `Cord::Chars()` to retrieve this iterator. + // Returns a `Cord::CharRange` for iterating over the characters of a `Cord` + // with a range-based for-loop. For most character-based iteration tasks on a + // Cord, use `Cord::Chars()` to retrieve this iterator. // // Example: // @@ -615,6 +751,29 @@ class Cord { cord->Append(part); } + // Cord::SetExpectedChecksum() + // + // Stores a checksum value with this non-empty cord instance, for later + // retrieval. + // + // The expected checksum is a number stored out-of-band, alongside the data. + // It is preserved across copies and assignments, but any mutations to a cord + // will cause it to lose its expected checksum. + // + // The expected checksum is not part of a Cord's value, and does not affect + // operations such as equality or hashing. + // + // This field is intended to store a CRC32C checksum for later validation, to + // help support end-to-end checksum workflows. However, the Cord API itself + // does no CRC validation, and assigns no meaning to this number. + // + // This call has no effect if this cord is empty. + void SetExpectedChecksum(uint32_t crc); + + // Returns this cord's expected checksum, if it has one. Otherwise, returns + // nullopt. + absl::optional ExpectedChecksum() const; + template friend H AbslHashValue(H hash_state, const absl::Cord& c) { absl::optional maybe_flat = c.TryFlat(); @@ -624,11 +783,34 @@ class Cord { return c.HashFragmented(std::move(hash_state)); } + // Create a Cord with the contents of StringConstant::value. + // No allocations will be done and no data will be copied. + // This is an INTERNAL API and subject to change or removal. This API can only + // be used by spelling absl::strings_internal::MakeStringConstant, which is + // also an internal API. + template + // NOLINTNEXTLINE(google-explicit-constructor) + constexpr Cord(strings_internal::StringConstant); + private: + using CordRep = absl::cord_internal::CordRep; + using CordRepFlat = absl::cord_internal::CordRepFlat; + using CordzInfo = cord_internal::CordzInfo; + using CordzUpdateScope = cord_internal::CordzUpdateScope; + using CordzUpdateTracker = cord_internal::CordzUpdateTracker; + using InlineData = cord_internal::InlineData; + using MethodIdentifier = CordzUpdateTracker::MethodIdentifier; + + // Creates a cord instance with `method` representing the originating + // public API call causing the cord to be created. + explicit Cord(absl::string_view src, MethodIdentifier method); + friend class CordTestPeer; friend bool operator==(const Cord& lhs, const Cord& rhs); friend bool operator==(const Cord& lhs, absl::string_view rhs); + friend const CordzInfo* GetCordzInfoForTesting(const Cord& cord); + // Calls the provided function once for each cord chunk, in order. Unlike // Chunks(), this API will not allocate memory. void ForEachChunk(absl::FunctionRef) const; @@ -644,96 +826,138 @@ class Cord { // InlineRep holds either a tree pointer, or an array of kMaxInline bytes. class InlineRep { public: - static constexpr unsigned char kMaxInline = 15; + static constexpr unsigned char kMaxInline = cord_internal::kMaxInline; static_assert(kMaxInline >= sizeof(absl::cord_internal::CordRep*), ""); - // Tag byte & kMaxInline means we are storing a pointer. - static constexpr unsigned char kTreeFlag = 1 << 4; - // Tag byte & kProfiledFlag means we are profiling the Cord. - static constexpr unsigned char kProfiledFlag = 1 << 5; - constexpr InlineRep() : data_{} {} + constexpr InlineRep() : data_() {} + explicit InlineRep(InlineData::DefaultInitType init) : data_(init) {} InlineRep(const InlineRep& src); InlineRep(InlineRep&& src); InlineRep& operator=(const InlineRep& src); InlineRep& operator=(InlineRep&& src) noexcept; + explicit constexpr InlineRep(absl::string_view sv, CordRep* rep); + void Swap(InlineRep* rhs); bool empty() const; size_t size() const; const char* data() const; // Returns nullptr if holding pointer - void set_data(const char* data, size_t n, - bool nullify_tail); // Discards pointer, if any - char* set_data(size_t n); // Write data to the result + void set_data(const char* data, size_t n); // Discards pointer, if any + char* set_data(size_t n); // Write data to the result // Returns nullptr if holding bytes absl::cord_internal::CordRep* tree() const; - // Discards old pointer, if any - void set_tree(absl::cord_internal::CordRep* rep); - // Replaces a tree with a new root. This is faster than set_tree, but it - // should only be used when it's clear that the old rep was a tree. - void replace_tree(absl::cord_internal::CordRep* rep); + absl::cord_internal::CordRep* as_tree() const; + const char* as_chars() const; // Returns non-null iff was holding a pointer absl::cord_internal::CordRep* clear(); // Converts to pointer if necessary. - absl::cord_internal::CordRep* force_tree(size_t extra_hint); - void reduce_size(size_t n); // REQUIRES: holding data + void reduce_size(size_t n); // REQUIRES: holding data void remove_prefix(size_t n); // REQUIRES: holding data - void AppendArray(const char* src_data, size_t src_size); + void AppendArray(absl::string_view src, MethodIdentifier method); absl::string_view FindFlatStartPiece() const; - void AppendTree(absl::cord_internal::CordRep* tree); - void PrependTree(absl::cord_internal::CordRep* tree); - void GetAppendRegion(char** region, size_t* size, size_t max_length); - void GetAppendRegion(char** region, size_t* size); - bool IsSame(const InlineRep& other) const { - return memcmp(data_, other.data_, sizeof(data_)) == 0; - } - int BitwiseCompare(const InlineRep& other) const { - uint64_t x, y; - // Use memcpy to avoid anti-aliasing issues. - memcpy(&x, data_, sizeof(x)); - memcpy(&y, other.data_, sizeof(y)); - if (x == y) { - memcpy(&x, data_ + 8, sizeof(x)); - memcpy(&y, other.data_ + 8, sizeof(y)); - if (x == y) return 0; - } - return absl::big_endian::FromHost64(x) < absl::big_endian::FromHost64(y) - ? -1 - : 1; - } + + // Creates a CordRepFlat instance from the current inlined data with `extra' + // bytes of desired additional capacity. + CordRepFlat* MakeFlatWithExtraCapacity(size_t extra); + + // Sets the tree value for this instance. `rep` must not be null. + // Requires the current instance to hold a tree, and a lock to be held on + // any CordzInfo referenced by this instance. The latter is enforced through + // the CordzUpdateScope argument. If the current instance is sampled, then + // the CordzInfo instance is updated to reference the new `rep` value. + void SetTree(CordRep* rep, const CordzUpdateScope& scope); + + // Identical to SetTree(), except that `rep` is allowed to be null, in + // which case the current instance is reset to an empty value. + void SetTreeOrEmpty(CordRep* rep, const CordzUpdateScope& scope); + + // Sets the tree value for this instance, and randomly samples this cord. + // This function disregards existing contents in `data_`, and should be + // called when a Cord is 'promoted' from an 'uninitialized' or 'inlined' + // value to a non-inlined (tree / ring) value. + void EmplaceTree(CordRep* rep, MethodIdentifier method); + + // Identical to EmplaceTree, except that it copies the parent stack from + // the provided `parent` data if the parent is sampled. + void EmplaceTree(CordRep* rep, const InlineData& parent, + MethodIdentifier method); + + // Commits the change of a newly created, or updated `rep` root value into + // this cord. `old_rep` indicates the old (inlined or tree) value of the + // cord, and determines if the commit invokes SetTree() or EmplaceTree(). + void CommitTree(const CordRep* old_rep, CordRep* rep, + const CordzUpdateScope& scope, MethodIdentifier method); + + void AppendTreeToInlined(CordRep* tree, MethodIdentifier method); + void AppendTreeToTree(CordRep* tree, MethodIdentifier method); + void AppendTree(CordRep* tree, MethodIdentifier method); + void PrependTreeToInlined(CordRep* tree, MethodIdentifier method); + void PrependTreeToTree(CordRep* tree, MethodIdentifier method); + void PrependTree(CordRep* tree, MethodIdentifier method); + + bool IsSame(const InlineRep& other) const { return data_ == other.data_; } + void CopyTo(std::string* dst) const { // memcpy is much faster when operating on a known size. On most supported // platforms, the small string optimization is large enough that resizing // to 15 bytes does not cause a memory allocation. - absl::strings_internal::STLStringResizeUninitialized(dst, - sizeof(data_) - 1); - memcpy(&(*dst)[0], data_, sizeof(data_) - 1); + absl::strings_internal::STLStringResizeUninitialized(dst, kMaxInline); + data_.copy_max_inline_to(&(*dst)[0]); // erase is faster than resize because the logic for memory allocation is // not needed. - dst->erase(data_[kMaxInline]); + dst->erase(inline_size()); } // Copies the inline contents into `dst`. Assumes the cord is not empty. void CopyToArray(char* dst) const; - bool is_tree() const { return data_[kMaxInline] > kMaxInline; } + bool is_tree() const { return data_.is_tree(); } + + // Returns true if the Cord is being profiled by cordz. + bool is_profiled() const { return data_.is_tree() && data_.is_profiled(); } + + // Returns the available inlined capacity, or 0 if is_tree() == true. + size_t remaining_inline_capacity() const { + return data_.is_tree() ? 0 : kMaxInline - data_.inline_size(); + } + + // Returns the profiled CordzInfo, or nullptr if not sampled. + absl::cord_internal::CordzInfo* cordz_info() const { + return data_.cordz_info(); + } + + // Sets the profiled CordzInfo. `cordz_info` must not be null. + void set_cordz_info(cord_internal::CordzInfo* cordz_info) { + assert(cordz_info != nullptr); + data_.set_cordz_info(cordz_info); + } + + // Resets the current cordz_info to null / empty. + void clear_cordz_info() { data_.clear_cordz_info(); } private: friend class Cord; void AssignSlow(const InlineRep& src); - // Unrefs the tree, stops profiling, and zeroes the contents - void ClearSlow(); + // Unrefs the tree and stops profiling. + void UnrefTree(); + + void ResetToEmpty() { data_ = {}; } - // If the data has length <= kMaxInline, we store it in data_[0..len-1], - // and store the length in data_[kMaxInline]. Else we store it in a tree - // and store a pointer to that tree in data_[0..sizeof(CordRep*)-1]. - alignas(absl::cord_internal::CordRep*) char data_[kMaxInline + 1]; + void set_inline_size(size_t size) { data_.set_inline_size(size); } + size_t inline_size() const { return data_.inline_size(); } + + // Empty cords that carry a checksum have a CordRepCrc node with a null + // child node. The code can avoid lots of special cases where it would + // otherwise transition from tree to inline storage if we just remove the + // CordRepCrc node before mutations. Must never be called inside a + // CordzUpdateScope since it untracks the cordz info. + void MaybeRemoveEmptyCrcNode(); + + cord_internal::InlineData data_; }; InlineRep contents_; - // Helper for MemoryUsage(). - static size_t MemoryUsageAux(const absl::cord_internal::CordRep* rep); - // Helper for GetFlat() and TryFlat(). static bool GetFlatAux(absl::cord_internal::CordRep* rep, absl::string_view* fragment); @@ -771,6 +995,24 @@ class Cord { template void AppendImpl(C&& src); + // Appends / Prepends `src` to this instance, using precise sizing. + // This method does explicitly not attempt to use any spare capacity + // in any pending last added private owned flat. + // Requires `src` to be <= kMaxFlatLength. + void AppendPrecise(absl::string_view src, MethodIdentifier method); + void PrependPrecise(absl::string_view src, MethodIdentifier method); + + CordBuffer GetAppendBufferSlowPath(size_t block_size, size_t capacity, + size_t min_capacity); + + // Prepends the provided data to this instance. `method` contains the public + // API method for this action which is tracked for Cordz sampling purposes. + void PrependArray(absl::string_view src, MethodIdentifier method); + + // Assigns the value in 'src' to this instance, 'stealing' its contents. + // Requires src.length() > kMaxBytesToCopy. + Cord& AssignLargeString(std::string&& src); + // Helper for AbslHashValue(). template H HashFragmented(H hash_state) const { @@ -781,6 +1023,10 @@ class Cord { }); return H::combine(combiner.finalize(std::move(hash_state)), size()); } + + friend class CrcCord; + void SetCrcCordState(crc_internal::CrcCordState state); + const crc_internal::CrcCordState* MaybeGetCrcCordState() const; }; ABSL_NAMESPACE_END @@ -797,63 +1043,17 @@ extern std::ostream& operator<<(std::ostream& out, const Cord& cord); namespace cord_internal { -// Fast implementation of memmove for up to 15 bytes. This implementation is -// safe for overlapping regions. If nullify_tail is true, the destination is -// padded with '\0' up to 16 bytes. -inline void SmallMemmove(char* dst, const char* src, size_t n, - bool nullify_tail = false) { - if (n >= 8) { - assert(n <= 16); - uint64_t buf1; - uint64_t buf2; - memcpy(&buf1, src, 8); - memcpy(&buf2, src + n - 8, 8); - if (nullify_tail) { - memset(dst + 8, 0, 8); - } - memcpy(dst, &buf1, 8); - memcpy(dst + n - 8, &buf2, 8); - } else if (n >= 4) { - uint32_t buf1; - uint32_t buf2; - memcpy(&buf1, src, 4); - memcpy(&buf2, src + n - 4, 4); - if (nullify_tail) { - memset(dst + 4, 0, 4); - memset(dst + 8, 0, 8); - } - memcpy(dst, &buf1, 4); - memcpy(dst + n - 4, &buf2, 4); - } else { - if (n != 0) { - dst[0] = src[0]; - dst[n / 2] = src[n / 2]; - dst[n - 1] = src[n - 1]; - } - if (nullify_tail) { - memset(dst + 8, 0, 8); - memset(dst + n, 0, 8); - } - } -} - // Does non-template-specific `CordRepExternal` initialization. -// Expects `data` to be non-empty. +// Requires `data` to be non-empty. void InitializeCordRepExternal(absl::string_view data, CordRepExternal* rep); // Creates a new `CordRep` that owns `data` and `releaser` and returns a pointer -// to it, or `nullptr` if `data` was empty. +// to it. Requires `data` to be non-empty. template // NOLINTNEXTLINE - suppress clang-tidy raw pointer return. CordRep* NewExternalRep(absl::string_view data, Releaser&& releaser) { + assert(!data.empty()); using ReleaserType = absl::decay_t; - if (data.empty()) { - // Never create empty external nodes. - InvokeReleaser(Rank0{}, ReleaserType(std::forward(releaser)), - data); - return nullptr; - } - CordRepExternal* rep = new CordRepExternalImpl( std::forward(releaser), 0); InitializeCordRepExternal(data, rep); @@ -873,18 +1073,34 @@ inline CordRep* NewExternalRep(absl::string_view data, template Cord MakeCordFromExternal(absl::string_view data, Releaser&& releaser) { Cord cord; - cord.contents_.set_tree(::absl::cord_internal::NewExternalRep( - data, std::forward(releaser))); + if (ABSL_PREDICT_TRUE(!data.empty())) { + cord.contents_.EmplaceTree(::absl::cord_internal::NewExternalRep( + data, std::forward(releaser)), + Cord::MethodIdentifier::kMakeCordFromExternal); + } else { + using ReleaserType = absl::decay_t; + cord_internal::InvokeReleaser( + cord_internal::Rank0{}, ReleaserType(std::forward(releaser)), + data); + } return cord; } -inline Cord::InlineRep::InlineRep(const Cord::InlineRep& src) { - cord_internal::SmallMemmove(data_, src.data_, sizeof(data_)); +constexpr Cord::InlineRep::InlineRep(absl::string_view sv, CordRep* rep) + : data_(sv, rep) {} + +inline Cord::InlineRep::InlineRep(const Cord::InlineRep& src) + : data_(InlineData::kDefaultInit) { + if (CordRep* tree = src.tree()) { + EmplaceTree(CordRep::Ref(tree), src.data_, + CordzUpdateTracker::kConstructorCord); + } else { + data_ = src.data_; + } } -inline Cord::InlineRep::InlineRep(Cord::InlineRep&& src) { - memcpy(data_, src.data_, sizeof(data_)); - memset(src.data_, 0, sizeof(data_)); +inline Cord::InlineRep::InlineRep(Cord::InlineRep&& src) : data_(src.data_) { + src.ResetToEmpty(); } inline Cord::InlineRep& Cord::InlineRep::operator=(const Cord::InlineRep& src) { @@ -892,7 +1108,7 @@ inline Cord::InlineRep& Cord::InlineRep::operator=(const Cord::InlineRep& src) { return *this; } if (!is_tree() && !src.is_tree()) { - cord_internal::SmallMemmove(data_, src.data_, sizeof(data_)); + data_ = src.data_; return *this; } AssignSlow(src); @@ -902,10 +1118,10 @@ inline Cord::InlineRep& Cord::InlineRep::operator=(const Cord::InlineRep& src) { inline Cord::InlineRep& Cord::InlineRep::operator=( Cord::InlineRep&& src) noexcept { if (is_tree()) { - ClearSlow(); + UnrefTree(); } - memcpy(data_, src.data_, sizeof(data_)); - memset(src.data_, 0, sizeof(data_)); + data_ = src.data_; + src.ResetToEmpty(); return *this; } @@ -913,82 +1129,147 @@ inline void Cord::InlineRep::Swap(Cord::InlineRep* rhs) { if (rhs == this) { return; } - - Cord::InlineRep tmp; - cord_internal::SmallMemmove(tmp.data_, data_, sizeof(data_)); - cord_internal::SmallMemmove(data_, rhs->data_, sizeof(data_)); - cord_internal::SmallMemmove(rhs->data_, tmp.data_, sizeof(data_)); + std::swap(data_, rhs->data_); } inline const char* Cord::InlineRep::data() const { - return is_tree() ? nullptr : data_; + return is_tree() ? nullptr : data_.as_chars(); +} + +inline const char* Cord::InlineRep::as_chars() const { + assert(!data_.is_tree()); + return data_.as_chars(); +} + +inline absl::cord_internal::CordRep* Cord::InlineRep::as_tree() const { + assert(data_.is_tree()); + return data_.as_tree(); } inline absl::cord_internal::CordRep* Cord::InlineRep::tree() const { if (is_tree()) { - absl::cord_internal::CordRep* rep; - memcpy(&rep, data_, sizeof(rep)); - return rep; + return as_tree(); } else { return nullptr; } } -inline bool Cord::InlineRep::empty() const { return data_[kMaxInline] == 0; } +inline bool Cord::InlineRep::empty() const { return data_.is_empty(); } inline size_t Cord::InlineRep::size() const { - const char tag = data_[kMaxInline]; - if (tag <= kMaxInline) return tag; - return static_cast(tree()->length); + return is_tree() ? as_tree()->length : inline_size(); } -inline void Cord::InlineRep::set_tree(absl::cord_internal::CordRep* rep) { - if (rep == nullptr) { - memset(data_, 0, sizeof(data_)); +inline cord_internal::CordRepFlat* Cord::InlineRep::MakeFlatWithExtraCapacity( + size_t extra) { + static_assert(cord_internal::kMinFlatLength >= sizeof(data_), ""); + size_t len = data_.inline_size(); + auto* result = CordRepFlat::New(len + extra); + result->length = len; + data_.copy_max_inline_to(result->Data()); + return result; +} + +inline void Cord::InlineRep::EmplaceTree(CordRep* rep, + MethodIdentifier method) { + assert(rep); + data_.make_tree(rep); + CordzInfo::MaybeTrackCord(data_, method); +} + +inline void Cord::InlineRep::EmplaceTree(CordRep* rep, const InlineData& parent, + MethodIdentifier method) { + data_.make_tree(rep); + CordzInfo::MaybeTrackCord(data_, parent, method); +} + +inline void Cord::InlineRep::SetTree(CordRep* rep, + const CordzUpdateScope& scope) { + assert(rep); + assert(data_.is_tree()); + data_.set_tree(rep); + scope.SetCordRep(rep); +} + +inline void Cord::InlineRep::SetTreeOrEmpty(CordRep* rep, + const CordzUpdateScope& scope) { + assert(data_.is_tree()); + if (rep) { + data_.set_tree(rep); } else { - bool was_tree = is_tree(); - memcpy(data_, &rep, sizeof(rep)); - memset(data_ + sizeof(rep), 0, sizeof(data_) - sizeof(rep) - 1); - if (!was_tree) { - data_[kMaxInline] = kTreeFlag; - } + data_ = {}; } + scope.SetCordRep(rep); } -inline void Cord::InlineRep::replace_tree(absl::cord_internal::CordRep* rep) { - ABSL_ASSERT(is_tree()); - if (ABSL_PREDICT_FALSE(rep == nullptr)) { - set_tree(rep); - return; +inline void Cord::InlineRep::CommitTree(const CordRep* old_rep, CordRep* rep, + const CordzUpdateScope& scope, + MethodIdentifier method) { + if (old_rep) { + SetTree(rep, scope); + } else { + EmplaceTree(rep, method); } - memcpy(data_, &rep, sizeof(rep)); - memset(data_ + sizeof(rep), 0, sizeof(data_) - sizeof(rep) - 1); } inline absl::cord_internal::CordRep* Cord::InlineRep::clear() { - const char tag = data_[kMaxInline]; - absl::cord_internal::CordRep* result = nullptr; - if (tag > kMaxInline) { - memcpy(&result, data_, sizeof(result)); + if (is_tree()) { + CordzInfo::MaybeUntrackCord(cordz_info()); } - memset(data_, 0, sizeof(data_)); // Clear the cord + absl::cord_internal::CordRep* result = tree(); + ResetToEmpty(); return result; } inline void Cord::InlineRep::CopyToArray(char* dst) const { assert(!is_tree()); - size_t n = data_[kMaxInline]; + size_t n = inline_size(); assert(n != 0); - cord_internal::SmallMemmove(dst, data_, n); + cord_internal::SmallMemmove(dst, data_.as_chars(), n); +} + +inline void Cord::InlineRep::MaybeRemoveEmptyCrcNode() { + CordRep* rep = tree(); + if (rep == nullptr || ABSL_PREDICT_TRUE(rep->length > 0)) { + return; + } + assert(rep->IsCrc()); + assert(rep->crc()->child == nullptr); + CordzInfo::MaybeUntrackCord(cordz_info()); + CordRep::Unref(rep); + ResetToEmpty(); } constexpr inline Cord::Cord() noexcept {} +inline Cord::Cord(absl::string_view src) + : Cord(src, CordzUpdateTracker::kConstructorString) {} + +template +constexpr Cord::Cord(strings_internal::StringConstant) + : contents_(strings_internal::StringConstant::value, + strings_internal::StringConstant::value.size() <= + cord_internal::kMaxInline + ? nullptr + : &cord_internal::ConstInitExternalStorage< + strings_internal::StringConstant>::value) {} + inline Cord& Cord::operator=(const Cord& x) { contents_ = x.contents_; return *this; } +template > +Cord& Cord::operator=(T&& src) { + if (src.size() <= cord_internal::kMaxBytesToCopy) { + return operator=(absl::string_view(src)); + } else { + return AssignLargeString(std::forward(src)); + } +} + +inline Cord::Cord(const Cord& src) : contents_(src.contents_) {} + inline Cord::Cord(Cord&& src) noexcept : contents_(std::move(src.contents_)) {} inline void Cord::swap(Cord& other) noexcept { @@ -1001,19 +1282,29 @@ inline Cord& Cord::operator=(Cord&& x) noexcept { } extern template Cord::Cord(std::string&& src); -extern template Cord& Cord::operator=(std::string&& src); inline size_t Cord::size() const { // Length is 1st field in str.rep_ return contents_.size(); } -inline bool Cord::empty() const { return contents_.empty(); } +inline bool Cord::empty() const { return size() == 0; } -inline size_t Cord::EstimatedMemoryUsage() const { +inline size_t Cord::EstimatedMemoryUsage( + CordMemoryAccounting accounting_method) const { size_t result = sizeof(Cord); if (const absl::cord_internal::CordRep* rep = contents_.tree()) { - result += MemoryUsageAux(rep); + switch (accounting_method) { + case CordMemoryAccounting::kFairShare: + result += cord_internal::GetEstimatedFairShareMemoryUsage(rep); + break; + case CordMemoryAccounting::kTotalMorePrecise: + result += cord_internal::GetMorePreciseMemoryUsage(rep); + break; + case CordMemoryAccounting::kTotal: + result += cord_internal::GetEstimatedMemoryUsage(rep); + break; + } } return result; } @@ -1044,7 +1335,46 @@ inline absl::string_view Cord::Flatten() { } inline void Cord::Append(absl::string_view src) { - contents_.AppendArray(src.data(), src.size()); + contents_.AppendArray(src, CordzUpdateTracker::kAppendString); +} + +inline void Cord::Prepend(absl::string_view src) { + PrependArray(src, CordzUpdateTracker::kPrependString); +} + +inline void Cord::Append(CordBuffer buffer) { + if (ABSL_PREDICT_FALSE(buffer.length() == 0)) return; + absl::string_view short_value; + if (CordRep* rep = buffer.ConsumeValue(short_value)) { + contents_.AppendTree(rep, CordzUpdateTracker::kAppendCordBuffer); + } else { + AppendPrecise(short_value, CordzUpdateTracker::kAppendCordBuffer); + } +} + +inline void Cord::Prepend(CordBuffer buffer) { + if (ABSL_PREDICT_FALSE(buffer.length() == 0)) return; + absl::string_view short_value; + if (CordRep* rep = buffer.ConsumeValue(short_value)) { + contents_.PrependTree(rep, CordzUpdateTracker::kPrependCordBuffer); + } else { + PrependPrecise(short_value, CordzUpdateTracker::kPrependCordBuffer); + } +} + +inline CordBuffer Cord::GetAppendBuffer(size_t capacity, size_t min_capacity) { + if (empty()) return CordBuffer::CreateWithDefaultLimit(capacity); + return GetAppendBufferSlowPath(0, capacity, min_capacity); +} + +inline CordBuffer Cord::GetCustomAppendBuffer(size_t block_size, + size_t capacity, + size_t min_capacity) { + if (empty()) { + return block_size ? CordBuffer::CreateWithCustomLimit(block_size, capacity) + : CordBuffer::CreateWithDefaultLimit(capacity); + } + return GetAppendBufferSlowPath(block_size, capacity, min_capacity); } extern template void Cord::Append(std::string&& src); @@ -1052,7 +1382,7 @@ extern template void Cord::Prepend(std::string&& src); inline int Cord::Compare(const Cord& rhs) const { if (!contents_.is_tree() && !rhs.contents_.is_tree()) { - return contents_.BitwiseCompare(rhs.contents_); + return contents_.data_.Compare(rhs.contents_.data_); } return CompareImpl(rhs); @@ -1072,17 +1402,71 @@ inline bool Cord::StartsWith(absl::string_view rhs) const { return EqualsImpl(rhs, rhs_size); } -inline Cord::ChunkIterator::ChunkIterator(const Cord* cord) - : bytes_remaining_(cord->size()) { - if (cord->empty()) return; - if (cord->contents_.is_tree()) { - stack_of_right_children_.push_back(cord->contents_.tree()); - operator++(); +inline void Cord::ChunkIterator::InitTree(cord_internal::CordRep* tree) { + tree = cord_internal::SkipCrcNode(tree); + if (tree->tag == cord_internal::BTREE) { + current_chunk_ = btree_reader_.Init(tree->btree()); + } else { + current_leaf_ = tree; + current_chunk_ = cord_internal::EdgeData(tree); + } +} + +inline Cord::ChunkIterator::ChunkIterator(cord_internal::CordRep* tree) { + bytes_remaining_ = tree->length; + InitTree(tree); +} + +inline Cord::ChunkIterator::ChunkIterator(const Cord* cord) { + if (CordRep* tree = cord->contents_.tree()) { + bytes_remaining_ = tree->length; + if (ABSL_PREDICT_TRUE(bytes_remaining_ != 0)) { + InitTree(tree); + } else { + current_chunk_ = {}; + } + } else { + bytes_remaining_ = cord->contents_.inline_size(); + current_chunk_ = {cord->contents_.data(), bytes_remaining_}; + } +} + +inline Cord::ChunkIterator& Cord::ChunkIterator::AdvanceBtree() { + current_chunk_ = btree_reader_.Next(); + return *this; +} + +inline void Cord::ChunkIterator::AdvanceBytesBtree(size_t n) { + assert(n >= current_chunk_.size()); + bytes_remaining_ -= n; + if (bytes_remaining_) { + if (n == current_chunk_.size()) { + current_chunk_ = btree_reader_.Next(); + } else { + size_t offset = btree_reader_.length() - bytes_remaining_; + current_chunk_ = btree_reader_.Seek(offset); + } } else { - current_chunk_ = absl::string_view(cord->contents_.data(), cord->size()); + current_chunk_ = {}; } } +inline Cord::ChunkIterator& Cord::ChunkIterator::operator++() { + ABSL_HARDENING_ASSERT(bytes_remaining_ > 0 && + "Attempted to iterate past `end()`"); + assert(bytes_remaining_ >= current_chunk_.size()); + bytes_remaining_ -= current_chunk_.size(); + if (bytes_remaining_ > 0) { + if (btree_reader_) { + return AdvanceBtree(); + } else { + assert(!current_chunk_.empty()); // Called on invalid iterator. + } + current_chunk_ = {}; + } + return *this; +} + inline Cord::ChunkIterator Cord::ChunkIterator::operator++(int) { ChunkIterator tmp(*this); operator++(); @@ -1114,10 +1498,15 @@ inline void Cord::ChunkIterator::RemoveChunkPrefix(size_t n) { } inline void Cord::ChunkIterator::AdvanceBytes(size_t n) { + assert(bytes_remaining_ >= n); if (ABSL_PREDICT_TRUE(n < current_chunk_.size())) { RemoveChunkPrefix(n); } else if (n != 0) { - AdvanceBytesSlowPath(n); + if (btree_reader_) { + AdvanceBytesBtree(n); + } else { + bytes_remaining_ = 0; + } } } @@ -1204,11 +1593,11 @@ inline void Cord::ForEachChunk( if (rep == nullptr) { callback(absl::string_view(contents_.data(), contents_.size())); } else { - return ForEachChunkAux(rep, callback); + ForEachChunkAux(rep, callback); } } -// Nonmember Cord-to-Cord relational operarators. +// Nonmember Cord-to-Cord relational operators. inline bool operator==(const Cord& lhs, const Cord& rhs) { if (lhs.contents_.IsSame(rhs.contents_)) return true; size_t rhs_size = rhs.size(); @@ -1217,12 +1606,8 @@ inline bool operator==(const Cord& lhs, const Cord& rhs) { } inline bool operator!=(const Cord& x, const Cord& y) { return !(x == y); } -inline bool operator<(const Cord& x, const Cord& y) { - return x.Compare(y) < 0; -} -inline bool operator>(const Cord& x, const Cord& y) { - return x.Compare(y) > 0; -} +inline bool operator<(const Cord& x, const Cord& y) { return x.Compare(y) < 0; } +inline bool operator>(const Cord& x, const Cord& y) { return x.Compare(y) > 0; } inline bool operator<=(const Cord& x, const Cord& y) { return x.Compare(y) <= 0; } @@ -1263,7 +1648,6 @@ class CordTestAccess { public: static size_t FlatOverhead(); static size_t MaxFlatLength(); - static size_t SizeofCordRepConcat(); static size_t SizeofCordRepExternal(); static size_t SizeofCordRepSubstring(); static size_t FlatTagToLength(uint8_t tag); diff --git a/Siv3D/include/ThirdParty/absl/strings/cord_analysis.h b/Siv3D/include/ThirdParty/absl/strings/cord_analysis.h new file mode 100644 index 0000000000..9b9527a57d --- /dev/null +++ b/Siv3D/include/ThirdParty/absl/strings/cord_analysis.h @@ -0,0 +1,62 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_CORD_ANALYSIS_H_ +#define ABSL_STRINGS_CORD_ANALYSIS_H_ + +#include +#include + +#include "absl/base/config.h" +#include "absl/strings/internal/cord_internal.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// Returns the *approximate* number of bytes held in full or in part by this +// Cord (which may not remain the same between invocations). Cords that share +// memory could each be "charged" independently for the same shared memory. +size_t GetEstimatedMemoryUsage(const CordRep* rep); + +// Returns the *approximate* number of bytes held in full or in part by this +// Cord for the distinct memory held by this cord. This is similar to +// `GetEstimatedMemoryUsage()`, except that if the cord has multiple references +// to the same memory, that memory is only counted once. +// +// For example: +// absl::Cord cord; +// cord.append(some_other_cord); +// cord.append(some_other_cord); +// // Calls GetEstimatedMemoryUsage() and counts `other_cord` twice: +// cord.EstimatedMemoryUsage(kTotal); +// // Calls GetMorePreciseMemoryUsage() and counts `other_cord` once: +// cord.EstimatedMemoryUsage(kTotalMorePrecise); +// +// This is more expensive than `GetEstimatedMemoryUsage()` as it requires +// deduplicating all memory references. +size_t GetMorePreciseMemoryUsage(const CordRep* rep); + +// Returns the *approximate* number of bytes held in full or in part by this +// CordRep weighted by the sharing ratio of that data. For example, if some data +// edge is shared by 4 different Cords, then each cord is attribute 1/4th of +// the total memory usage as a 'fair share' of the total memory usage. +size_t GetEstimatedFairShareMemoryUsage(const CordRep* rep); + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl + + +#endif // ABSL_STRINGS_CORD_ANALYSIS_H_ diff --git a/Siv3D/include/ThirdParty/absl/strings/cord_buffer.h b/Siv3D/include/ThirdParty/absl/strings/cord_buffer.h new file mode 100644 index 0000000000..bc0e4e45f4 --- /dev/null +++ b/Siv3D/include/ThirdParty/absl/strings/cord_buffer.h @@ -0,0 +1,572 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: cord_buffer.h +// ----------------------------------------------------------------------------- +// +// This file defines an `absl::CordBuffer` data structure to hold data for +// eventual inclusion within an existing `Cord` data structure. Cord buffers are +// useful for building large Cords that may require custom allocation of its +// associated memory. +// +#ifndef ABSL_STRINGS_CORD_BUFFER_H_ +#define ABSL_STRINGS_CORD_BUFFER_H_ + +#include +#include +#include +#include +#include +#include + +#include "absl/base/config.h" +#include "absl/base/macros.h" +#include "absl/numeric/bits.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_flat.h" +#include "absl/types/span.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +class Cord; +class CordBufferTestPeer; + +// CordBuffer +// +// CordBuffer manages memory buffers for purposes such as zero-copy APIs as well +// as applications building cords with large data requiring granular control +// over the allocation and size of cord data. For example, a function creating +// a cord of random data could use a CordBuffer as follows: +// +// absl::Cord CreateRandomCord(size_t length) { +// absl::Cord cord; +// while (length > 0) { +// CordBuffer buffer = CordBuffer::CreateWithDefaultLimit(length); +// absl::Span data = buffer.available_up_to(length); +// FillRandomValues(data.data(), data.size()); +// buffer.IncreaseLengthBy(data.size()); +// cord.Append(std::move(buffer)); +// length -= data.size(); +// } +// return cord; +// } +// +// CordBuffer instances are by default limited to a capacity of `kDefaultLimit` +// bytes. `kDefaultLimit` is currently just under 4KiB, but this default may +// change in the future and/or for specific architectures. The default limit is +// aimed to provide a good trade-off between performance and memory overhead. +// Smaller buffers typically incur more compute cost while larger buffers are +// more CPU efficient but create significant memory overhead because of such +// allocations being less granular. Using larger buffers may also increase the +// risk of memory fragmentation. +// +// Applications create a buffer using one of the `CreateWithDefaultLimit()` or +// `CreateWithCustomLimit()` methods. The returned instance will have a non-zero +// capacity and a zero length. Applications use the `data()` method to set the +// contents of the managed memory, and once done filling the buffer, use the +// `IncreaseLengthBy()` or 'SetLength()' method to specify the length of the +// initialized data before adding the buffer to a Cord. +// +// The `CreateWithCustomLimit()` method is intended for applications needing +// larger buffers than the default memory limit, allowing the allocation of up +// to a capacity of `kCustomLimit` bytes minus some minimum internal overhead. +// The usage of `CreateWithCustomLimit()` should be limited to only those use +// cases where the distribution of the input is relatively well known, and/or +// where the trade-off between the efficiency gains outweigh the risk of memory +// fragmentation. See the documentation for `CreateWithCustomLimit()` for more +// information on using larger custom limits. +// +// The capacity of a `CordBuffer` returned by one of the `Create` methods may +// be larger than the requested capacity due to rounding, alignment and +// granularity of the memory allocator. Applications should use the `capacity` +// method to obtain the effective capacity of the returned instance as +// demonstrated in the provided example above. +// +// CordBuffer is a move-only class. All references into the managed memory are +// invalidated when an instance is moved into either another CordBuffer instance +// or a Cord. Writing to a location obtained by a previous call to `data()` +// after an instance was moved will lead to undefined behavior. +// +// A `moved from` CordBuffer instance will have a valid, but empty state. +// CordBuffer is thread compatible. +class CordBuffer { + public: + // kDefaultLimit + // + // Default capacity limits of allocated CordBuffers. + // See the class comments for more information on allocation limits. + static constexpr size_t kDefaultLimit = cord_internal::kMaxFlatLength; + + // kCustomLimit + // + // Maximum size for CreateWithCustomLimit() allocated buffers. + // Note that the effective capacity may be slightly less + // because of internal overhead of internal cord buffers. + static constexpr size_t kCustomLimit = 64U << 10; + + // Constructors, Destructors and Assignment Operators + + // Creates an empty CordBuffer. + CordBuffer() = default; + + // Destroys this CordBuffer instance and, if not empty, releases any memory + // managed by this instance, invalidating previously returned references. + ~CordBuffer(); + + // CordBuffer is move-only + CordBuffer(CordBuffer&& rhs) noexcept; + CordBuffer& operator=(CordBuffer&&) noexcept; + CordBuffer(const CordBuffer&) = delete; + CordBuffer& operator=(const CordBuffer&) = delete; + + // CordBuffer::MaximumPayload() + // + // Returns the guaranteed maximum payload for a CordBuffer returned by the + // `CreateWithDefaultLimit()` method. While small, each internal buffer inside + // a Cord incurs an overhead to manage the length, type and reference count + // for the buffer managed inside the cord tree. Applications can use this + // method to get approximate number of buffers required for a given byte + // size, etc. + // + // For example: + // const size_t payload = absl::CordBuffer::MaximumPayload(); + // const size_t buffer_count = (total_size + payload - 1) / payload; + // buffers.reserve(buffer_count); + static constexpr size_t MaximumPayload(); + + // Overload to the above `MaximumPayload()` except that it returns the + // maximum payload for a CordBuffer returned by the `CreateWithCustomLimit()` + // method given the provided `block_size`. + static constexpr size_t MaximumPayload(size_t block_size); + + // CordBuffer::CreateWithDefaultLimit() + // + // Creates a CordBuffer instance of the desired `capacity`, capped at the + // default limit `kDefaultLimit`. The returned buffer has a guaranteed + // capacity of at least `min(kDefaultLimit, capacity)`. See the class comments + // for more information on buffer capacities and intended usage. + static CordBuffer CreateWithDefaultLimit(size_t capacity); + + // CordBuffer::CreateWithCustomLimit() + // + // Creates a CordBuffer instance of the desired `capacity` rounded to an + // appropriate power of 2 size less than, or equal to `block_size`. + // Requires `block_size` to be a power of 2. + // + // If `capacity` is less than or equal to `kDefaultLimit`, then this method + // behaves identical to `CreateWithDefaultLimit`, which means that the caller + // is guaranteed to get a buffer of at least the requested capacity. + // + // If `capacity` is greater than or equal to `block_size`, then this method + // returns a buffer with an `allocated size` of `block_size` bytes. Otherwise, + // this methods returns a buffer with a suitable smaller power of 2 block size + // to satisfy the request. The actual size depends on a number of factors, and + // is typically (but not necessarily) the highest or second highest power of 2 + // value less than or equal to `capacity`. + // + // The 'allocated size' includes a small amount of overhead required for + // internal state, which is currently 13 bytes on 64-bit platforms. For + // example: a buffer created with `block_size` and `capacity' set to 8KiB + // will have an allocated size of 8KiB, and an effective internal `capacity` + // of 8KiB - 13 = 8179 bytes. + // + // To demonstrate this in practice, let's assume we want to read data from + // somewhat larger files using approximately 64KiB buffers: + // + // absl::Cord ReadFromFile(int fd, size_t n) { + // absl::Cord cord; + // while (n > 0) { + // CordBuffer buffer = CordBuffer::CreateWithCustomLimit(64 << 10, n); + // absl::Span data = buffer.available_up_to(n); + // ReadFileDataOrDie(fd, data.data(), data.size()); + // buffer.IncreaseLengthBy(data.size()); + // cord.Append(std::move(buffer)); + // n -= data.size(); + // } + // return cord; + // } + // + // If we'd use this function to read a file of 659KiB, we may get the + // following pattern of allocated cord buffer sizes: + // + // CreateWithCustomLimit(64KiB, 674816) --> ~64KiB (65523) + // CreateWithCustomLimit(64KiB, 674816) --> ~64KiB (65523) + // ... + // CreateWithCustomLimit(64KiB, 19586) --> ~16KiB (16371) + // CreateWithCustomLimit(64KiB, 3215) --> 3215 (at least 3215) + // + // The reason the method returns a 16K buffer instead of a roughly 19K buffer + // is to reduce memory overhead and fragmentation risks. Using carefully + // chosen power of 2 values reduces the entropy of allocated memory sizes. + // + // Additionally, let's assume we'd use the above function on files that are + // generally smaller than 64K. If we'd use 'precise' sized buffers for such + // files, than we'd get a very wide distribution of allocated memory sizes + // rounded to 4K page sizes, and we'd end up with a lot of unused capacity. + // + // In general, application should only use custom sizes if the data they are + // consuming or storing is expected to be many times the chosen block size, + // and be based on objective data and performance metrics. For example, a + // compress function may work faster and consume less CPU when using larger + // buffers. Such an application should pick a size offering a reasonable + // trade-off between expected data size, compute savings with larger buffers, + // and the cost or fragmentation effect of larger buffers. + // Applications must pick a reasonable spot on that curve, and make sure their + // data meets their expectations in size distributions such as "mostly large". + static CordBuffer CreateWithCustomLimit(size_t block_size, size_t capacity); + + // CordBuffer::available() + // + // Returns the span delineating the available capacity in this buffer + // which is defined as `{ data() + length(), capacity() - length() }`. + absl::Span available(); + + // CordBuffer::available_up_to() + // + // Returns the span delineating the available capacity in this buffer limited + // to `size` bytes. This is equivalent to `available().subspan(0, size)`. + absl::Span available_up_to(size_t size); + + // CordBuffer::data() + // + // Returns a non-null reference to the data managed by this instance. + // Applications are allowed to write up to `capacity` bytes of instance data. + // CordBuffer data is uninitialized by default. Reading data from an instance + // that has not yet been initialized will lead to undefined behavior. + char* data(); + const char* data() const; + + // CordBuffer::length() + // + // Returns the length of this instance. The default length of a CordBuffer is + // 0, indicating an 'empty' CordBuffer. Applications must specify the length + // of the data in a CordBuffer before adding it to a Cord. + size_t length() const; + + // CordBuffer::capacity() + // + // Returns the capacity of this instance. All instances have a non-zero + // capacity: default and `moved from` instances have a small internal buffer. + size_t capacity() const; + + // CordBuffer::IncreaseLengthBy() + // + // Increases the length of this buffer by the specified 'n' bytes. + // Applications must make sure all data in this buffer up to the new length + // has been initialized before adding a CordBuffer to a Cord: failure to do so + // will lead to undefined behavior. Requires `length() + n <= capacity()`. + // Typically, applications will use 'available_up_to()` to get a span of the + // desired capacity, and use `span.size()` to increase the length as in: + // absl::Span span = buffer.available_up_to(desired); + // buffer.IncreaseLengthBy(span.size()); + // memcpy(span.data(), src, span.size()); + // etc... + void IncreaseLengthBy(size_t n); + + // CordBuffer::SetLength() + // + // Sets the data length of this instance. Applications must make sure all data + // of the specified length has been initialized before adding a CordBuffer to + // a Cord: failure to do so will lead to undefined behavior. + // Setting the length to a small value or zero does not release any memory + // held by this CordBuffer instance. Requires `length <= capacity()`. + // Applications should preferably use the `IncreaseLengthBy()` method above + // in combination with the 'available()` or `available_up_to()` methods. + void SetLength(size_t length); + + private: + // Make sure we don't accidentally over promise. + static_assert(kCustomLimit <= cord_internal::kMaxLargeFlatSize, ""); + + // Assume the cost of an 'uprounded' allocation to CeilPow2(size) versus + // the cost of allocating at least 1 extra flat <= 4KB: + // - Flat overhead = 13 bytes + // - Btree amortized cost / node =~ 13 bytes + // - 64 byte granularity of tcmalloc at 4K =~ 32 byte average + // CPU cost and efficiency requires we should at least 'save' something by + // splitting, as a poor man's measure, we say the slop needs to be + // at least double the cost offset to make it worth splitting: ~128 bytes. + static constexpr size_t kMaxPageSlop = 128; + + // Overhead for allocation a flat. + static constexpr size_t kOverhead = cord_internal::kFlatOverhead; + + using CordRepFlat = cord_internal::CordRepFlat; + + // `Rep` is the internal data representation of a CordBuffer. The internal + // representation has an internal small size optimization similar to + // std::string (SSO). + struct Rep { + // Inline SSO size of a CordBuffer + static constexpr size_t kInlineCapacity = sizeof(intptr_t) * 2 - 1; + + // Creates a default instance with kInlineCapacity. + Rep() : short_rep{} {} + + // Creates an instance managing an allocated non zero CordRep. + explicit Rep(cord_internal::CordRepFlat* rep) : long_rep{rep} { + assert(rep != nullptr); + } + + // Returns true if this instance manages the SSO internal buffer. + bool is_short() const { + constexpr size_t offset = offsetof(Short, raw_size); + return (reinterpret_cast(this)[offset] & 1) != 0; + } + + // Returns the available area of the internal SSO data + absl::Span short_available() { + const size_t length = short_length(); + return absl::Span(short_rep.data + length, + kInlineCapacity - length); + } + + // Returns the available area of the internal SSO data + absl::Span long_available() const { + assert(!is_short()); + const size_t length = long_rep.rep->length; + return absl::Span(long_rep.rep->Data() + length, + long_rep.rep->Capacity() - length); + } + + // Returns the length of the internal SSO data. + size_t short_length() const { + assert(is_short()); + return static_cast(short_rep.raw_size >> 1); + } + + // Sets the length of the internal SSO data. + // Disregards any previously set CordRep instance. + void set_short_length(size_t length) { + short_rep.raw_size = static_cast((length << 1) + 1); + } + + // Adds `n` to the current short length. + void add_short_length(size_t n) { + assert(is_short()); + short_rep.raw_size += static_cast(n << 1); + } + + // Returns reference to the internal SSO data buffer. + char* data() { + assert(is_short()); + return short_rep.data; + } + const char* data() const { + assert(is_short()); + return short_rep.data; + } + + // Returns a pointer the external CordRep managed by this instance. + cord_internal::CordRepFlat* rep() const { + assert(!is_short()); + return long_rep.rep; + } + + // The internal representation takes advantage of the fact that allocated + // memory is always on an even address, and uses the least significant bit + // of the first or last byte (depending on endianness) as the inline size + // indicator overlapping with the least significant byte of the CordRep*. +#if defined(ABSL_IS_BIG_ENDIAN) + struct Long { + explicit Long(cord_internal::CordRepFlat* rep_arg) : rep(rep_arg) {} + void* padding; + cord_internal::CordRepFlat* rep; + }; + struct Short { + char data[sizeof(Long) - 1]; + char raw_size = 1; + }; +#else + struct Long { + explicit Long(cord_internal::CordRepFlat* rep_arg) : rep(rep_arg) {} + cord_internal::CordRepFlat* rep; + void* padding; + }; + struct Short { + char raw_size = 1; + char data[sizeof(Long) - 1]; + }; +#endif + + union { + Long long_rep; + Short short_rep; + }; + }; + + // Power2 functions + static bool IsPow2(size_t size) { return absl::has_single_bit(size); } + static size_t Log2Floor(size_t size) { + return static_cast(absl::bit_width(size) - 1); + } + static size_t Log2Ceil(size_t size) { + return static_cast(absl::bit_width(size - 1)); + } + + // Implementation of `CreateWithCustomLimit()`. + // This implementation allows for future memory allocation hints to + // be passed down into the CordRepFlat allocation function. + template + static CordBuffer CreateWithCustomLimitImpl(size_t block_size, + size_t capacity, + AllocationHints... hints); + + // Consumes the value contained in this instance and resets the instance. + // This method returns a non-null Cordrep* if the current instances manages a + // CordRep*, and resets the instance to an empty SSO instance. If the current + // instance is an SSO instance, then this method returns nullptr and sets + // `short_value` to the inlined data value. In either case, the current + // instance length is reset to zero. + // This method is intended to be used by Cord internal functions only. + cord_internal::CordRep* ConsumeValue(absl::string_view& short_value) { + cord_internal::CordRep* rep = nullptr; + if (rep_.is_short()) { + short_value = absl::string_view(rep_.data(), rep_.short_length()); + } else { + rep = rep_.rep(); + } + rep_.set_short_length(0); + return rep; + } + + // Internal constructor. + explicit CordBuffer(cord_internal::CordRepFlat* rep) : rep_(rep) { + assert(rep != nullptr); + } + + Rep rep_; + + friend class Cord; + friend class CordBufferTestPeer; +}; + +inline constexpr size_t CordBuffer::MaximumPayload() { + return cord_internal::kMaxFlatLength; +} + +inline constexpr size_t CordBuffer::MaximumPayload(size_t block_size) { + return (std::min)(kCustomLimit, block_size) - cord_internal::kFlatOverhead; +} + +inline CordBuffer CordBuffer::CreateWithDefaultLimit(size_t capacity) { + if (capacity > Rep::kInlineCapacity) { + auto* rep = cord_internal::CordRepFlat::New(capacity); + rep->length = 0; + return CordBuffer(rep); + } + return CordBuffer(); +} + +template +inline CordBuffer CordBuffer::CreateWithCustomLimitImpl( + size_t block_size, size_t capacity, AllocationHints... hints) { + assert(IsPow2(block_size)); + capacity = (std::min)(capacity, kCustomLimit); + block_size = (std::min)(block_size, kCustomLimit); + if (capacity + kOverhead >= block_size) { + capacity = block_size; + } else if (capacity <= kDefaultLimit) { + capacity = capacity + kOverhead; + } else if (!IsPow2(capacity)) { + // Check if rounded up to next power 2 is a good enough fit + // with limited waste making it an acceptable direct fit. + const size_t rounded_up = size_t{1} << Log2Ceil(capacity); + const size_t slop = rounded_up - capacity; + if (slop >= kOverhead && slop <= kMaxPageSlop + kOverhead) { + capacity = rounded_up; + } else { + // Round down to highest power of 2 <= capacity. + // Consider a more aggressive step down if that may reduce the + // risk of fragmentation where 'people are holding it wrong'. + const size_t rounded_down = size_t{1} << Log2Floor(capacity); + capacity = rounded_down; + } + } + const size_t length = capacity - kOverhead; + auto* rep = CordRepFlat::New(CordRepFlat::Large(), length, hints...); + rep->length = 0; + return CordBuffer(rep); +} + +inline CordBuffer CordBuffer::CreateWithCustomLimit(size_t block_size, + size_t capacity) { + return CreateWithCustomLimitImpl(block_size, capacity); +} + +inline CordBuffer::~CordBuffer() { + if (!rep_.is_short()) { + cord_internal::CordRepFlat::Delete(rep_.rep()); + } +} + +inline CordBuffer::CordBuffer(CordBuffer&& rhs) noexcept : rep_(rhs.rep_) { + rhs.rep_.set_short_length(0); +} + +inline CordBuffer& CordBuffer::operator=(CordBuffer&& rhs) noexcept { + if (!rep_.is_short()) cord_internal::CordRepFlat::Delete(rep_.rep()); + rep_ = rhs.rep_; + rhs.rep_.set_short_length(0); + return *this; +} + +inline absl::Span CordBuffer::available() { + return rep_.is_short() ? rep_.short_available() : rep_.long_available(); +} + +inline absl::Span CordBuffer::available_up_to(size_t size) { + return available().subspan(0, size); +} + +inline char* CordBuffer::data() { + return rep_.is_short() ? rep_.data() : rep_.rep()->Data(); +} + +inline const char* CordBuffer::data() const { + return rep_.is_short() ? rep_.data() : rep_.rep()->Data(); +} + +inline size_t CordBuffer::capacity() const { + return rep_.is_short() ? Rep::kInlineCapacity : rep_.rep()->Capacity(); +} + +inline size_t CordBuffer::length() const { + return rep_.is_short() ? rep_.short_length() : rep_.rep()->length; +} + +inline void CordBuffer::SetLength(size_t length) { + ABSL_HARDENING_ASSERT(length <= capacity()); + if (rep_.is_short()) { + rep_.set_short_length(length); + } else { + rep_.rep()->length = length; + } +} + +inline void CordBuffer::IncreaseLengthBy(size_t n) { + ABSL_HARDENING_ASSERT(n <= capacity() && length() + n <= capacity()); + if (rep_.is_short()) { + rep_.add_short_length(n); + } else { + rep_.rep()->length += n; + } +} + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_CORD_BUFFER_H_ diff --git a/Siv3D/include/ThirdParty/absl/strings/cord_test_helpers.h b/Siv3D/include/ThirdParty/absl/strings/cord_test_helpers.h index f1036e3b13..ca52240a14 100644 --- a/Siv3D/include/ThirdParty/absl/strings/cord_test_helpers.h +++ b/Siv3D/include/ThirdParty/absl/strings/cord_test_helpers.h @@ -17,11 +17,73 @@ #ifndef ABSL_STRINGS_CORD_TEST_HELPERS_H_ #define ABSL_STRINGS_CORD_TEST_HELPERS_H_ +#include +#include +#include + +#include "absl/base/config.h" #include "absl/strings/cord.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/string_view.h" namespace absl { ABSL_NAMESPACE_BEGIN +// Cord sizes relevant for testing +enum class TestCordSize { + // An empty value + kEmpty = 0, + + // An inlined string value + kInlined = cord_internal::kMaxInline / 2 + 1, + + // 'Well known' SSO lengths (excluding terminating zero). + // libstdcxx has a maximum SSO of 15, libc++ has a maximum SSO of 22. + kStringSso1 = 15, + kStringSso2 = 22, + + // A string value which is too large to fit in inlined data, but small enough + // such that Cord prefers copying the value if possible, i.e.: not stealing + // std::string inputs, or referencing existing CordReps on Append, etc. + kSmall = cord_internal::kMaxBytesToCopy / 2 + 1, + + // A string value large enough that Cord prefers to reference or steal from + // existing inputs rather than copying contents of the input. + kMedium = cord_internal::kMaxFlatLength / 2 + 1, + + // A string value large enough to cause it to be stored in multiple flats. + kLarge = cord_internal::kMaxFlatLength * 4 +}; + +// To string helper +inline absl::string_view ToString(TestCordSize size) { + switch (size) { + case TestCordSize::kEmpty: + return "Empty"; + case TestCordSize::kInlined: + return "Inlined"; + case TestCordSize::kSmall: + return "Small"; + case TestCordSize::kStringSso1: + return "StringSso1"; + case TestCordSize::kStringSso2: + return "StringSso2"; + case TestCordSize::kMedium: + return "Medium"; + case TestCordSize::kLarge: + return "Large"; + } + return "???"; +} + +// Returns the length matching the specified size +inline size_t Length(TestCordSize size) { return static_cast(size); } + +// Stream output helper +inline std::ostream& operator<<(std::ostream& stream, TestCordSize size) { + return stream << ToString(size); +} + // Creates a multi-segment Cord from an iterable container of strings. The // resulting Cord is guaranteed to have one segment for every string in the // container. This allows code to be unit tested with multi-segment Cord diff --git a/Siv3D/include/ThirdParty/absl/strings/cordz_test_helpers.h b/Siv3D/include/ThirdParty/absl/strings/cordz_test_helpers.h new file mode 100644 index 0000000000..e410eecf7f --- /dev/null +++ b/Siv3D/include/ThirdParty/absl/strings/cordz_test_helpers.h @@ -0,0 +1,151 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_CORDZ_TEST_HELPERS_H_ +#define ABSL_STRINGS_CORDZ_TEST_HELPERS_H_ + +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/config.h" +#include "absl/base/macros.h" +#include "absl/strings/cord.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cordz_info.h" +#include "absl/strings/internal/cordz_sample_token.h" +#include "absl/strings/internal/cordz_statistics.h" +#include "absl/strings/internal/cordz_update_tracker.h" +#include "absl/strings/str_cat.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// Returns the CordzInfo for the cord, or nullptr if the cord is not sampled. +inline const cord_internal::CordzInfo* GetCordzInfoForTesting( + const Cord& cord) { + if (!cord.contents_.is_tree()) return nullptr; + return cord.contents_.cordz_info(); +} + +// Returns true if the provided cordz_info is in the list of sampled cords. +inline bool CordzInfoIsListed(const cord_internal::CordzInfo* cordz_info, + cord_internal::CordzSampleToken token = {}) { + for (const cord_internal::CordzInfo& info : token) { + if (cordz_info == &info) return true; + } + return false; +} + +// Matcher on Cord that verifies all of: +// - the cord is sampled +// - the CordzInfo of the cord is listed / discoverable. +// - the reported CordzStatistics match the cord's actual properties +// - the cord has an (initial) UpdateTracker count of 1 for `method` +MATCHER_P(HasValidCordzInfoOf, method, "CordzInfo matches cord") { + const cord_internal::CordzInfo* cord_info = GetCordzInfoForTesting(arg); + if (cord_info == nullptr) { + *result_listener << "cord is not sampled"; + return false; + } + if (!CordzInfoIsListed(cord_info)) { + *result_listener << "cord is sampled, but not listed"; + return false; + } + cord_internal::CordzStatistics stat = cord_info->GetCordzStatistics(); + if (stat.size != arg.size()) { + *result_listener << "cordz size " << stat.size + << " does not match cord size " << arg.size(); + return false; + } + if (stat.update_tracker.Value(method) != 1) { + *result_listener << "Expected method count 1 for " << method << ", found " + << stat.update_tracker.Value(method); + return false; + } + return true; +} + +// Matcher on Cord that verifies that the cord is sampled and that the CordzInfo +// update tracker has 'method' with a call count of 'n' +MATCHER_P2(CordzMethodCountEq, method, n, + absl::StrCat("CordzInfo method count equals ", n)) { + const cord_internal::CordzInfo* cord_info = GetCordzInfoForTesting(arg); + if (cord_info == nullptr) { + *result_listener << "cord is not sampled"; + return false; + } + cord_internal::CordzStatistics stat = cord_info->GetCordzStatistics(); + if (stat.update_tracker.Value(method) != n) { + *result_listener << "Expected method count " << n << " for " << method + << ", found " << stat.update_tracker.Value(method); + return false; + } + return true; +} + +// Cordz will only update with a new rate once the previously scheduled event +// has fired. When we disable Cordz, a long delay takes place where we won't +// consider profiling new Cords. CordzSampleIntervalHelper will burn through +// that interval and allow for testing that assumes that the average sampling +// interval is a particular value. +class CordzSamplingIntervalHelper { + public: + explicit CordzSamplingIntervalHelper(int32_t interval) + : orig_mean_interval_(absl::cord_internal::get_cordz_mean_interval()) { + absl::cord_internal::set_cordz_mean_interval(interval); + absl::cord_internal::cordz_set_next_sample_for_testing(interval); + } + + ~CordzSamplingIntervalHelper() { + absl::cord_internal::set_cordz_mean_interval(orig_mean_interval_); + absl::cord_internal::cordz_set_next_sample_for_testing(orig_mean_interval_); + } + + private: + int32_t orig_mean_interval_; +}; + +// Wrapper struct managing a small CordRep `rep` +struct TestCordRep { + cord_internal::CordRepFlat* rep; + + TestCordRep() { + rep = cord_internal::CordRepFlat::New(100); + rep->length = 100; + memset(rep->Data(), 1, 100); + } + ~TestCordRep() { cord_internal::CordRep::Unref(rep); } +}; + +// Wrapper struct managing a small CordRep `rep`, and +// an InlineData `data` initialized with that CordRep. +struct TestCordData { + TestCordRep rep; + cord_internal::InlineData data{rep.rep}; +}; + +// Creates a Cord that is not sampled +template +Cord UnsampledCord(Args... args) { + CordzSamplingIntervalHelper never(9999); + Cord cord(std::forward(args)...); + ABSL_ASSERT(GetCordzInfoForTesting(cord) == nullptr); + return cord; +} + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_CORDZ_TEST_HELPERS_H_ diff --git a/Siv3D/include/ThirdParty/absl/strings/escaping.h b/Siv3D/include/ThirdParty/absl/strings/escaping.h index f5ca26c5da..bf2a58980a 100644 --- a/Siv3D/include/ThirdParty/absl/strings/escaping.h +++ b/Siv3D/include/ThirdParty/absl/strings/escaping.h @@ -117,35 +117,40 @@ std::string Utf8SafeCEscape(absl::string_view src); // conversion. std::string Utf8SafeCHexEscape(absl::string_view src); -// Base64Unescape() -// -// Converts a `src` string encoded in Base64 to its binary equivalent, writing -// it to a `dest` buffer, returning `true` on success. If `src` contains invalid -// characters, `dest` is cleared and returns `false`. -bool Base64Unescape(absl::string_view src, std::string* dest); - -// WebSafeBase64Unescape() -// -// Converts a `src` string encoded in Base64 to its binary equivalent, writing -// it to a `dest` buffer, but using '-' instead of '+', and '_' instead of '/'. -// If `src` contains invalid characters, `dest` is cleared and returns `false`. -bool WebSafeBase64Unescape(absl::string_view src, std::string* dest); - // Base64Escape() // -// Encodes a `src` string into a base64-encoded string, with padding characters. -// This function conforms with RFC 4648 section 4 (base64). +// Encodes a `src` string into a base64-encoded 'dest' string with padding +// characters. This function conforms with RFC 4648 section 4 (base64) and RFC +// 2045. void Base64Escape(absl::string_view src, std::string* dest); std::string Base64Escape(absl::string_view src); // WebSafeBase64Escape() // -// Encodes a `src` string into a base64-like string, using '-' instead of '+' -// and '_' instead of '/', and without padding. This function conforms with RFC -// 4648 section 5 (base64url). +// Encodes a `src` string into a base64 string, like Base64Escape() does, but +// outputs '-' instead of '+' and '_' instead of '/', and does not pad 'dest'. +// This function conforms with RFC 4648 section 5 (base64url). void WebSafeBase64Escape(absl::string_view src, std::string* dest); std::string WebSafeBase64Escape(absl::string_view src); +// Base64Unescape() +// +// Converts a `src` string encoded in Base64 (RFC 4648 section 4) to its binary +// equivalent, writing it to a `dest` buffer, returning `true` on success. If +// `src` contains invalid characters, `dest` is cleared and returns `false`. +// If padding is included (note that `Base64Escape()` does produce it), it must +// be correct. In the padding, '=' and '.' are treated identically. +bool Base64Unescape(absl::string_view src, std::string* dest); + +// WebSafeBase64Unescape() +// +// Converts a `src` string encoded in "web safe" Base64 (RFC 4648 section 5) to +// its binary equivalent, writing it to a `dest` buffer. If `src` contains +// invalid characters, `dest` is cleared and returns `false`. If padding is +// included (note that `WebSafeBase64Escape()` does not produce it), it must be +// correct. In the padding, '=' and '.' are treated identically. +bool WebSafeBase64Unescape(absl::string_view src, std::string* dest); + // HexStringToBytes() // // Converts an ASCII hex string into bytes, returning binary data of length diff --git a/Siv3D/include/ThirdParty/absl/strings/internal/char_map.h b/Siv3D/include/ThirdParty/absl/strings/internal/char_map.h index 61484de0b7..70a9034336 100644 --- a/Siv3D/include/ThirdParty/absl/strings/internal/char_map.h +++ b/Siv3D/include/ThirdParty/absl/strings/internal/char_map.h @@ -73,10 +73,10 @@ class Charmap { } // Containing all the chars in the C-string 's'. - // Note that this is expensively recursive because of the C++11 constexpr - // formulation. Use only in constexpr initializers. static constexpr Charmap FromString(const char* s) { - return *s == 0 ? Charmap() : (Char(*s) | FromString(s + 1)); + Charmap ret; + while (*s) ret = ret | Char(*s++); + return ret; } // Containing all the chars in the closed interval [lo,hi]. @@ -103,10 +103,9 @@ class Charmap { constexpr Charmap(uint64_t b0, uint64_t b1, uint64_t b2, uint64_t b3) : m_{b0, b1, b2, b3} {} - static constexpr uint64_t RangeForWord(unsigned char lo, unsigned char hi, - uint64_t word) { - return OpenRangeFromZeroForWord(hi + 1, word) & - ~OpenRangeFromZeroForWord(lo, word); + static constexpr uint64_t RangeForWord(char lo, char hi, uint64_t word) { + return OpenRangeFromZeroForWord(static_cast(hi) + 1, word) & + ~OpenRangeFromZeroForWord(static_cast(lo), word); } // All the chars in the specified word of the range [0, upper). @@ -119,13 +118,16 @@ class Charmap { : (~static_cast(0) >> (64 - upper % 64)); } - static constexpr uint64_t CharMaskForWord(unsigned char x, uint64_t word) { - return (x / 64 == word) ? (static_cast(1) << (x % 64)) : 0; + static constexpr uint64_t CharMaskForWord(char x, uint64_t word) { + const auto unsigned_x = static_cast(x); + return (unsigned_x / 64 == word) + ? (static_cast(1) << (unsigned_x % 64)) + : 0; } - private: - void SetChar(unsigned char c) { - m_[c / 64] |= static_cast(1) << (c % 64); + void SetChar(char c) { + const auto unsigned_c = static_cast(c); + m_[unsigned_c / 64] |= static_cast(1) << (unsigned_c % 64); } uint64_t m_[4]; diff --git a/Siv3D/include/ThirdParty/absl/strings/internal/charconv_bigint.h b/Siv3D/include/ThirdParty/absl/strings/internal/charconv_bigint.h index 8f702976a8..5c0c375dbc 100644 --- a/Siv3D/include/ThirdParty/absl/strings/internal/charconv_bigint.h +++ b/Siv3D/include/ThirdParty/absl/strings/internal/charconv_bigint.h @@ -92,7 +92,7 @@ class BigUnsigned { // numbers with this many decimal digits or fewer are representable by this // type. // - // Analagous to std::numeric_limits::digits10. + // Analogous to std::numeric_limits::digits10. static constexpr int Digits10() { // 9975007/1035508 is very slightly less than log10(2**32). return static_cast(max_words) * 9975007 / 1035508; @@ -121,7 +121,7 @@ class BigUnsigned { ++size_; } } - std::fill(words_, words_ + word_shift, 0u); + std::fill_n(words_, word_shift, 0u); } } @@ -197,7 +197,7 @@ class BigUnsigned { } void SetToZero() { - std::fill(words_, words_ + size_, 0u); + std::fill_n(words_, size_, 0u); size_ = 0; } diff --git a/Siv3D/include/ThirdParty/absl/strings/internal/cord_data_edge.h b/Siv3D/include/ThirdParty/absl/strings/internal/cord_data_edge.h new file mode 100644 index 0000000000..e18b33e1b0 --- /dev/null +++ b/Siv3D/include/ThirdParty/absl/strings/internal/cord_data_edge.h @@ -0,0 +1,63 @@ +// Copyright 2022 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_INTERNAL_CORD_DATA_EDGE_H_ +#define ABSL_STRINGS_INTERNAL_CORD_DATA_EDGE_H_ + +#include +#include + +#include "absl/base/config.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_flat.h" +#include "absl/strings/string_view.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// Returns true if the provided rep is a FLAT, EXTERNAL or a SUBSTRING node +// holding a FLAT or EXTERNAL child rep. Requires `rep != nullptr`. +inline bool IsDataEdge(const CordRep* edge) { + assert(edge != nullptr); + + // The fast path is that `edge` is an EXTERNAL or FLAT node, making the below + // if a single, well predicted branch. We then repeat the FLAT or EXTERNAL + // check in the slow path of the SUBSTRING check to optimize for the hot path. + if (edge->tag == EXTERNAL || edge->tag >= FLAT) return true; + if (edge->tag == SUBSTRING) edge = edge->substring()->child; + return edge->tag == EXTERNAL || edge->tag >= FLAT; +} + +// Returns the `absl::string_view` data reference for the provided data edge. +// Requires 'IsDataEdge(edge) == true`. +inline absl::string_view EdgeData(const CordRep* edge) { + assert(IsDataEdge(edge)); + + size_t offset = 0; + const size_t length = edge->length; + if (edge->IsSubstring()) { + offset = edge->substring()->start; + edge = edge->substring()->child; + } + return edge->tag >= FLAT + ? absl::string_view{edge->flat()->Data() + offset, length} + : absl::string_view{edge->external()->base + offset, length}; +} + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_CORD_DATA_EDGE_H_ diff --git a/Siv3D/include/ThirdParty/absl/strings/internal/cord_internal.h b/Siv3D/include/ThirdParty/absl/strings/internal/cord_internal.h index d456eef838..20dd008cfa 100644 --- a/Siv3D/include/ThirdParty/absl/strings/internal/cord_internal.h +++ b/Siv3D/include/ThirdParty/absl/strings/internal/cord_internal.h @@ -1,4 +1,4 @@ -// Copyright 2020 The Abseil Authors. +// Copyright 2021 The Abseil Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -21,46 +21,162 @@ #include #include +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/base/internal/endian.h" #include "absl/base/internal/invoke.h" +#include "absl/base/optimization.h" #include "absl/container/internal/compressed_tuple.h" +#include "absl/container/internal/container_memory.h" #include "absl/meta/type_traits.h" #include "absl/strings/string_view.h" +// We can only add poisoning if we can detect consteval executions. +#if defined(ABSL_HAVE_CONSTANT_EVALUATED) && \ + (defined(ABSL_HAVE_ADDRESS_SANITIZER) || \ + defined(ABSL_HAVE_MEMORY_SANITIZER)) +#define ABSL_INTERNAL_CORD_HAVE_SANITIZER 1 +#endif + +#define ABSL_CORD_INTERNAL_NO_SANITIZE \ + ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY + namespace absl { ABSL_NAMESPACE_BEGIN namespace cord_internal { -// Wraps std::atomic for reference counting. -class Refcount { +// The overhead of a vtable is too much for Cord, so we roll our own subclasses +// using only a single byte to differentiate classes from each other - the "tag" +// byte. Define the subclasses first so we can provide downcasting helper +// functions in the base class. +struct CordRep; +struct CordRepConcat; +struct CordRepExternal; +struct CordRepFlat; +struct CordRepSubstring; +struct CordRepCrc; +class CordRepRing; +class CordRepBtree; + +class CordzInfo; + +// Default feature enable states for cord ring buffers +enum CordFeatureDefaults { + kCordEnableRingBufferDefault = false, + kCordShallowSubcordsDefault = false +}; + +extern std::atomic cord_ring_buffer_enabled; +extern std::atomic shallow_subcords_enabled; + +inline void enable_cord_ring_buffer(bool enable) { + cord_ring_buffer_enabled.store(enable, std::memory_order_relaxed); +} + +inline void enable_shallow_subcords(bool enable) { + shallow_subcords_enabled.store(enable, std::memory_order_relaxed); +} + +enum Constants { + // The inlined size to use with absl::InlinedVector. + // + // Note: The InlinedVectors in this file (and in cord.h) do not need to use + // the same value for their inlined size. The fact that they do is historical. + // It may be desirable for each to use a different inlined size optimized for + // that InlinedVector's usage. + // + // TODO(jgm): Benchmark to see if there's a more optimal value than 47 for + // the inlined vector size (47 exists for backward compatibility). + kInlinedVectorSize = 47, + + // Prefer copying blocks of at most this size, otherwise reference count. + kMaxBytesToCopy = 511 +}; + +// Emits a fatal error "Unexpected node type: xyz" and aborts the program. +ABSL_ATTRIBUTE_NORETURN void LogFatalNodeType(CordRep* rep); + +// Fast implementation of memmove for up to 15 bytes. This implementation is +// safe for overlapping regions. If nullify_tail is true, the destination is +// padded with '\0' up to 15 bytes. +template +inline void SmallMemmove(char* dst, const char* src, size_t n) { + if (n >= 8) { + assert(n <= 15); + uint64_t buf1; + uint64_t buf2; + memcpy(&buf1, src, 8); + memcpy(&buf2, src + n - 8, 8); + if (nullify_tail) { + memset(dst + 7, 0, 8); + } + memcpy(dst, &buf1, 8); + memcpy(dst + n - 8, &buf2, 8); + } else if (n >= 4) { + uint32_t buf1; + uint32_t buf2; + memcpy(&buf1, src, 4); + memcpy(&buf2, src + n - 4, 4); + if (nullify_tail) { + memset(dst + 4, 0, 4); + memset(dst + 7, 0, 8); + } + memcpy(dst, &buf1, 4); + memcpy(dst + n - 4, &buf2, 4); + } else { + if (n != 0) { + dst[0] = src[0]; + dst[n / 2] = src[n / 2]; + dst[n - 1] = src[n - 1]; + } + if (nullify_tail) { + memset(dst + 7, 0, 8); + memset(dst + n, 0, 8); + } + } +} + +// Compact class for tracking the reference count and state flags for CordRep +// instances. Data is stored in an atomic int32_t for compactness and speed. +class RefcountAndFlags { public: - Refcount() : count_{1} {} - ~Refcount() {} + constexpr RefcountAndFlags() : count_{kRefIncrement} {} + struct Immortal {}; + explicit constexpr RefcountAndFlags(Immortal) : count_(kImmortalFlag) {} - // Increments the reference count by 1. Imposes no memory ordering. - inline void Increment() { count_.fetch_add(1, std::memory_order_relaxed); } + // Increments the reference count. Imposes no memory ordering. + inline void Increment() { + count_.fetch_add(kRefIncrement, std::memory_order_relaxed); + } // Asserts that the current refcount is greater than 0. If the refcount is - // greater than 1, decrements the reference count by 1. + // greater than 1, decrements the reference count. // // Returns false if there are no references outstanding; true otherwise. // Inserts barriers to ensure that state written before this method returns // false will be visible to a thread that just observed this method returning - // false. + // false. Always returns false when the immortal bit is set. inline bool Decrement() { int32_t refcount = count_.load(std::memory_order_acquire); - assert(refcount > 0); - return refcount != 1 && count_.fetch_sub(1, std::memory_order_acq_rel) != 1; + assert((refcount & kRefcountMask) > 0 || refcount & kImmortalFlag); + return refcount != kRefIncrement && + (count_.fetch_sub(kRefIncrement, std::memory_order_acq_rel) & + kHighRefcountMask) != 0; } // Same as Decrement but expect that refcount is greater than 1. inline bool DecrementExpectHighRefcount() { - int32_t refcount = count_.fetch_sub(1, std::memory_order_acq_rel); - assert(refcount > 0); - return refcount != 1; + int32_t refcount = + count_.fetch_sub(kRefIncrement, std::memory_order_acq_rel); + assert((refcount & kRefcountMask) > 0 || refcount & kImmortalFlag); + return (refcount & kHighRefcountMask) != 0; } // Returns the current reference count using acquire semantics. - inline int32_t Get() const { return count_.load(std::memory_order_acquire); } + inline size_t Get() const { + return static_cast(count_.load(std::memory_order_acquire) >> + kNumFlags); + } // Returns whether the atomic integer is 1. // If the reference count is used in the conventional way, a @@ -69,51 +185,174 @@ class Refcount { // This call performs the test for a reference count of one, and // performs the memory barrier needed for the owning thread // to act on the object, knowing that it has exclusive access to the - // object. - inline bool IsOne() { return count_.load(std::memory_order_acquire) == 1; } + // object. Always returns false when the immortal bit is set. + inline bool IsOne() { + return (count_.load(std::memory_order_acquire) & kRefcountMask) == + kRefIncrement; + } + + bool IsImmortal() const { + return (count_.load(std::memory_order_relaxed) & kImmortalFlag) != 0; + } private: + // We reserve the bottom bits for flags. + // kImmortalBit indicates that this entity should never be collected; it is + // used for the StringConstant constructor to avoid collecting immutable + // constant cords. + // kReservedFlag is reserved for future use. + enum Flags { + kNumFlags = 2, + + kImmortalFlag = 0x1, + kReservedFlag = 0x2, + kRefIncrement = (1 << kNumFlags), + + // Bitmask to use when checking refcount by equality. This masks out + // all flags except kImmortalFlag, which is part of the refcount for + // purposes of equality. (A refcount of 0 or 1 does not count as 0 or 1 + // if the immortal bit is set.) + kRefcountMask = ~kReservedFlag, + + // Bitmask to use when checking if refcount is equal to 1 and not + // immortal when decrementing the refcount. This masks out kRefIncrement and + // all flags except kImmortalFlag. If the masked RefcountAndFlags is 0, we + // assume the refcount is equal to 1, since we know it's not immortal and + // not greater than 1. If the masked RefcountAndFlags is not 0, we can + // assume the refcount is not equal to 1 since either a higher bit in the + // refcount is set, or kImmortal is set. + kHighRefcountMask = kRefcountMask & ~kRefIncrement, + }; + std::atomic count_; }; -// The overhead of a vtable is too much for Cord, so we roll our own subclasses -// using only a single byte to differentiate classes from each other - the "tag" -// byte. Define the subclasses first so we can provide downcasting helper -// functions in the base class. +// Various representations that we allow +enum CordRepKind { + UNUSED_0 = 0, + SUBSTRING = 1, + CRC = 2, + BTREE = 3, + RING = 4, + EXTERNAL = 5, -struct CordRepConcat; -struct CordRepSubstring; -struct CordRepExternal; + // We have different tags for different sized flat arrays, + // starting with FLAT, and limited to MAX_FLAT_TAG. The below values map to an + // allocated range of 32 bytes to 256 KB. The current granularity is: + // - 8 byte granularity for flat sizes in [32 - 512] + // - 64 byte granularity for flat sizes in (512 - 8KiB] + // - 4KiB byte granularity for flat sizes in (8KiB, 256 KiB] + // If a new tag is needed in the future, then 'FLAT' and 'MAX_FLAT_TAG' should + // be adjusted as well as the Tag <---> Size mapping logic so that FLAT still + // represents the minimum flat allocation size. (32 bytes as of now). + FLAT = 6, + MAX_FLAT_TAG = 248 +}; + +// There are various locations where we want to check if some rep is a 'plain' +// data edge, i.e. an external or flat rep. By having FLAT == EXTERNAL + 1, we +// can perform this check in a single branch as 'tag >= EXTERNAL' +// Likewise, we have some locations where we check for 'ring or external/flat', +// so likewise align RING to EXTERNAL. +// Note that we can leave this optimization to the compiler. The compiler will +// DTRT when it sees a condition like `tag == EXTERNAL || tag >= FLAT`. +static_assert(RING == BTREE + 1, "BTREE and RING not consecutive"); +static_assert(EXTERNAL == RING + 1, "BTREE and EXTERNAL not consecutive"); +static_assert(FLAT == EXTERNAL + 1, "EXTERNAL and FLAT not consecutive"); struct CordRep { + // Result from an `extract edge` operation. Contains the (possibly changed) + // tree node as well as the extracted edge, or {tree, nullptr} if no edge + // could be extracted. + // On success, the returned `tree` value is null if `extracted` was the only + // data edge inside the tree, a data edge if there were only two data edges in + // the tree, or the (possibly new / smaller) remaining tree with the extracted + // data edge removed. + struct ExtractResult { + CordRep* tree; + CordRep* extracted; + }; + + CordRep() = default; + constexpr CordRep(RefcountAndFlags::Immortal immortal, size_t l) + : length(l), refcount(immortal), tag(EXTERNAL), storage{} {} + // The following three fields have to be less than 32 bytes since - // that is the smallest supported flat node size. + // that is the smallest supported flat node size. Some code optimizations rely + // on the specific layout of these fields. Notably: the non-trivial field + // `refcount` being preceded by `length`, and being tailed by POD data + // members only. + // # LINT.IfChange size_t length; - Refcount refcount; + RefcountAndFlags refcount; // If tag < FLAT, it represents CordRepKind and indicates the type of node. // Otherwise, the node type is CordRepFlat and the tag is the encoded size. uint8_t tag; - char data[1]; // Starting point for flat array: MUST BE LAST FIELD of CordRep - inline CordRepConcat* concat(); - inline const CordRepConcat* concat() const; + // `storage` provides two main purposes: + // - the starting point for FlatCordRep.Data() [flexible-array-member] + // - 3 bytes of additional storage for use by derived classes. + // The latter is used by CordrepConcat and CordRepBtree. CordRepConcat stores + // a 'depth' value in storage[0], and the (future) CordRepBtree class stores + // `height`, `begin` and `end` in the 3 entries. Otherwise we would need to + // allocate room for these in the derived class, as not all compilers reuse + // padding space from the base class (clang and gcc do, MSVC does not, etc) + uint8_t storage[3]; + // # LINT.ThenChange(cord_rep_btree.h:copy_raw) + + // Returns true if this instance's tag matches the requested type. + constexpr bool IsRing() const { return tag == RING; } + constexpr bool IsSubstring() const { return tag == SUBSTRING; } + constexpr bool IsCrc() const { return tag == CRC; } + constexpr bool IsExternal() const { return tag == EXTERNAL; } + constexpr bool IsFlat() const { return tag >= FLAT; } + constexpr bool IsBtree() const { return tag == BTREE; } + + inline CordRepRing* ring(); + inline const CordRepRing* ring() const; inline CordRepSubstring* substring(); inline const CordRepSubstring* substring() const; + inline CordRepCrc* crc(); + inline const CordRepCrc* crc() const; inline CordRepExternal* external(); inline const CordRepExternal* external() const; -}; + inline CordRepFlat* flat(); + inline const CordRepFlat* flat() const; + inline CordRepBtree* btree(); + inline const CordRepBtree* btree() const; + + // -------------------------------------------------------------------- + // Memory management + + // Destroys the provided `rep`. + static void Destroy(CordRep* rep); -struct CordRepConcat : public CordRep { - CordRep* left; - CordRep* right; + // Increments the reference count of `rep`. + // Requires `rep` to be a non-null pointer value. + static inline CordRep* Ref(CordRep* rep); - uint8_t depth() const { return static_cast(data[0]); } - void set_depth(uint8_t depth) { data[0] = static_cast(depth); } + // Decrements the reference count of `rep`. Destroys rep if count reaches + // zero. Requires `rep` to be a non-null pointer value. + static inline void Unref(CordRep* rep); }; struct CordRepSubstring : public CordRep { size_t start; // Starting offset of substring in child CordRep* child; + + // Creates a substring on `child`, adopting a reference on `child`. + // Requires `child` to be either a flat or external node, and `pos` and `n` to + // form a non-empty partial sub range of `'child`, i.e.: + // `n > 0 && n < length && n + pos <= length` + static inline CordRepSubstring* Create(CordRep* child, size_t pos, size_t n); + + // Creates a substring of `rep`. Does not adopt a reference on `rep`. + // Requires `IsDataEdge(rep) && n > 0 && pos + n <= rep->length`. + // If `n == rep->length` then this method returns `CordRep::Ref(rep)` + // If `rep` is a substring of a flat or external node, then this method will + // return a new substring of that flat or external node with `pos` adjusted + // with the original `start` position. + static inline CordRep* Substring(CordRep* rep, size_t pos, size_t n); }; // Type for function pointer that will invoke the releaser function and also @@ -124,9 +363,19 @@ using ExternalReleaserInvoker = void (*)(CordRepExternal*); // External CordReps are allocated together with a type erased releaser. The // releaser is stored in the memory directly following the CordRepExternal. struct CordRepExternal : public CordRep { + CordRepExternal() = default; + explicit constexpr CordRepExternal(absl::string_view str) + : CordRep(RefcountAndFlags::Immortal{}, str.size()), + base(str.data()), + releaser_invoker(nullptr) {} + const char* base; // Pointer to function that knows how to call and destroy the releaser. ExternalReleaserInvoker releaser_invoker; + + // Deletes (releases) the external rep. + // Requires rep != nullptr and rep->IsExternal() + static void Delete(CordRep* rep); }; struct Rank1 {}; @@ -167,7 +416,502 @@ struct CordRepExternalImpl } }; +inline CordRepSubstring* CordRepSubstring::Create(CordRep* child, size_t pos, + size_t n) { + assert(child != nullptr); + assert(n > 0); + assert(n < child->length); + assert(pos < child->length); + assert(n <= child->length - pos); + + // TODO(b/217376272): Harden internal logic. + // Move to strategical places inside the Cord logic and make this an assert. + if (ABSL_PREDICT_FALSE(!(child->IsExternal() || child->IsFlat()))) { + LogFatalNodeType(child); + } + + CordRepSubstring* rep = new CordRepSubstring(); + rep->length = n; + rep->tag = SUBSTRING; + rep->start = pos; + rep->child = child; + return rep; +} + +inline CordRep* CordRepSubstring::Substring(CordRep* rep, size_t pos, + size_t n) { + assert(rep != nullptr); + assert(n != 0); + assert(pos < rep->length); + assert(n <= rep->length - pos); + if (n == rep->length) return CordRep::Ref(rep); + if (rep->IsSubstring()) { + pos += rep->substring()->start; + rep = rep->substring()->child; + } + CordRepSubstring* substr = new CordRepSubstring(); + substr->length = n; + substr->tag = SUBSTRING; + substr->start = pos; + substr->child = CordRep::Ref(rep); + return substr; +} + +inline void CordRepExternal::Delete(CordRep* rep) { + assert(rep != nullptr && rep->IsExternal()); + auto* rep_external = static_cast(rep); + assert(rep_external->releaser_invoker != nullptr); + rep_external->releaser_invoker(rep_external); +} + +template +struct ConstInitExternalStorage { + ABSL_CONST_INIT static CordRepExternal value; +}; + +template +ABSL_CONST_INIT CordRepExternal + ConstInitExternalStorage::value(Str::value); + +enum { + kMaxInline = 15, +}; + +constexpr char GetOrNull(absl::string_view data, size_t pos) { + return pos < data.size() ? data[pos] : '\0'; +} + +// We store cordz_info as 64 bit pointer value in little endian format. This +// guarantees that the least significant byte of cordz_info matches the first +// byte of the inline data representation in `data`, which holds the inlined +// size or the 'is_tree' bit. +using cordz_info_t = int64_t; + +// Assert that the `cordz_info` pointer value perfectly overlaps the last half +// of `data` and can hold a pointer value. +static_assert(sizeof(cordz_info_t) * 2 == kMaxInline + 1, ""); +static_assert(sizeof(cordz_info_t) >= sizeof(intptr_t), ""); + +// LittleEndianByte() creates a little endian representation of 'value', i.e.: +// a little endian value where the first byte in the host's representation +// holds 'value`, with all other bytes being 0. +static constexpr cordz_info_t LittleEndianByte(unsigned char value) { +#if defined(ABSL_IS_BIG_ENDIAN) + return static_cast(value) << ((sizeof(cordz_info_t) - 1) * 8); +#else + return value; +#endif +} + +class InlineData { + public: + // DefaultInitType forces the use of the default initialization constructor. + enum DefaultInitType { kDefaultInit }; + + // kNullCordzInfo holds the little endian representation of intptr_t(1) + // This is the 'null' / initial value of 'cordz_info'. The null value + // is specifically big endian 1 as with 64-bit pointers, the last + // byte of cordz_info overlaps with the last byte holding the tag. + static constexpr cordz_info_t kNullCordzInfo = LittleEndianByte(1); + + // kTagOffset contains the offset of the control byte / tag. This constant is + // intended mostly for debugging purposes: do not remove this constant as it + // is actively inspected and used by gdb pretty printing code. + static constexpr size_t kTagOffset = 0; + + // Implement `~InlineData()` conditionally: we only need this destructor to + // unpoison poisoned instances under *SAN, and it will only compile correctly + // if the current compiler supports `absl::is_constant_evaluated()`. +#ifdef ABSL_INTERNAL_CORD_HAVE_SANITIZER + ~InlineData() noexcept { unpoison(); } +#endif + + constexpr InlineData() noexcept { poison_this(); } + + explicit InlineData(DefaultInitType) noexcept : rep_(kDefaultInit) { + poison_this(); + } + + explicit InlineData(CordRep* rep) noexcept : rep_(rep) { + ABSL_ASSERT(rep != nullptr); + } + + // Explicit constexpr constructor to create a constexpr InlineData + // value. Creates an inlined SSO value if `rep` is null, otherwise + // creates a tree instance value. + constexpr InlineData(absl::string_view sv, CordRep* rep) noexcept + : rep_(rep ? Rep(rep) : Rep(sv)) { + poison(); + } + + constexpr InlineData(const InlineData& rhs) noexcept; + InlineData& operator=(const InlineData& rhs) noexcept; + + friend bool operator==(const InlineData& lhs, const InlineData& rhs) { +#ifdef ABSL_INTERNAL_CORD_HAVE_SANITIZER + const Rep l = lhs.rep_.SanitizerSafeCopy(); + const Rep r = rhs.rep_.SanitizerSafeCopy(); + return memcmp(&l, &r, sizeof(l)) == 0; +#else + return memcmp(&lhs, &rhs, sizeof(lhs)) == 0; +#endif + } + friend bool operator!=(const InlineData& lhs, const InlineData& rhs) { + return !operator==(lhs, rhs); + } + + // Poisons the unused inlined SSO data if the current instance + // is inlined, else un-poisons the entire instance. + constexpr void poison(); + + // Un-poisons this instance. + constexpr void unpoison(); + + // Poisons the current instance. This is used on default initialization. + constexpr void poison_this(); + + // Returns true if the current instance is empty. + // The 'empty value' is an inlined data value of zero length. + bool is_empty() const { return rep_.tag() == 0; } + + // Returns true if the current instance holds a tree value. + bool is_tree() const { return (rep_.tag() & 1) != 0; } + + // Returns true if the current instance holds a cordz_info value. + // Requires the current instance to hold a tree value. + bool is_profiled() const { + assert(is_tree()); + return rep_.cordz_info() != kNullCordzInfo; + } + + // Returns true if either of the provided instances hold a cordz_info value. + // This method is more efficient than the equivalent `data1.is_profiled() || + // data2.is_profiled()`. Requires both arguments to hold a tree. + static bool is_either_profiled(const InlineData& data1, + const InlineData& data2) { + assert(data1.is_tree() && data2.is_tree()); + return (data1.rep_.cordz_info() | data2.rep_.cordz_info()) != + kNullCordzInfo; + } + + // Returns the cordz_info sampling instance for this instance, or nullptr + // if the current instance is not sampled and does not have CordzInfo data. + // Requires the current instance to hold a tree value. + CordzInfo* cordz_info() const { + assert(is_tree()); + intptr_t info = static_cast(absl::little_endian::ToHost64( + static_cast(rep_.cordz_info()))); + assert(info & 1); + return reinterpret_cast(info - 1); + } + + // Sets the current cordz_info sampling instance for this instance, or nullptr + // if the current instance is not sampled and does not have CordzInfo data. + // Requires the current instance to hold a tree value. + void set_cordz_info(CordzInfo* cordz_info) { + assert(is_tree()); + uintptr_t info = reinterpret_cast(cordz_info) | 1; + rep_.set_cordz_info( + static_cast(absl::little_endian::FromHost64(info))); + } + + // Resets the current cordz_info to null / empty. + void clear_cordz_info() { + assert(is_tree()); + rep_.set_cordz_info(kNullCordzInfo); + } + + // Returns a read only pointer to the character data inside this instance. + // Requires the current instance to hold inline data. + const char* as_chars() const { + assert(!is_tree()); + return rep_.as_chars(); + } + + // Returns a mutable pointer to the character data inside this instance. + // Should be used for 'write only' operations setting an inlined value. + // Applications can set the value of inlined data either before or after + // setting the inlined size, i.e., both of the below are valid: + // + // // Set inlined data and inline size + // memcpy(data_.as_chars(), data, size); + // data_.set_inline_size(size); + // + // // Set inlined size and inline data + // data_.set_inline_size(size); + // memcpy(data_.as_chars(), data, size); + // + // It's an error to read from the returned pointer without a preceding write + // if the current instance does not hold inline data, i.e.: is_tree() == true. + char* as_chars() { return rep_.as_chars(); } + + // Returns the tree value of this value. + // Requires the current instance to hold a tree value. + CordRep* as_tree() const { + assert(is_tree()); + return rep_.tree(); + } + + void set_inline_data(const char* data, size_t n) { + ABSL_ASSERT(n <= kMaxInline); + unpoison(); + rep_.set_tag(static_cast(n << 1)); + SmallMemmove(rep_.as_chars(), data, n); + poison(); + } + + void copy_max_inline_to(char* dst) const { + assert(!is_tree()); + memcpy(dst, rep_.SanitizerSafeCopy().as_chars(), kMaxInline); + } + + // Initialize this instance to holding the tree value `rep`, + // initializing the cordz_info to null, i.e.: 'not profiled'. + void make_tree(CordRep* rep) { + unpoison(); + rep_.make_tree(rep); + } + + // Set the tree value of this instance to 'rep`. + // Requires the current instance to already hold a tree value. + // Does not affect the value of cordz_info. + void set_tree(CordRep* rep) { + assert(is_tree()); + rep_.set_tree(rep); + } + + // Returns the size of the inlined character data inside this instance. + // Requires the current instance to hold inline data. + size_t inline_size() const { return rep_.inline_size(); } + + // Sets the size of the inlined character data inside this instance. + // Requires `size` to be <= kMaxInline. + // See the documentation on 'as_chars()' for more information and examples. + void set_inline_size(size_t size) { + unpoison(); + rep_.set_inline_size(size); + poison(); + } + + // Compares 'this' inlined data with rhs. The comparison is a straightforward + // lexicographic comparison. `Compare()` returns values as follows: + // + // -1 'this' InlineData instance is smaller + // 0 the InlineData instances are equal + // 1 'this' InlineData instance larger + int Compare(const InlineData& rhs) const { + return Compare(rep_.SanitizerSafeCopy(), rhs.rep_.SanitizerSafeCopy()); + } + + private: + struct Rep { + // See cordz_info_t for forced alignment and size of `cordz_info` details. + struct AsTree { + explicit constexpr AsTree(absl::cord_internal::CordRep* tree) + : rep(tree) {} + cordz_info_t cordz_info = kNullCordzInfo; + absl::cord_internal::CordRep* rep; + }; + + explicit Rep(DefaultInitType) {} + constexpr Rep() : data{0} {} + constexpr Rep(const Rep&) = default; + constexpr Rep& operator=(const Rep&) = default; + + explicit constexpr Rep(CordRep* rep) : as_tree(rep) {} + + explicit constexpr Rep(absl::string_view chars) + : data{static_cast((chars.size() << 1)), + GetOrNull(chars, 0), + GetOrNull(chars, 1), + GetOrNull(chars, 2), + GetOrNull(chars, 3), + GetOrNull(chars, 4), + GetOrNull(chars, 5), + GetOrNull(chars, 6), + GetOrNull(chars, 7), + GetOrNull(chars, 8), + GetOrNull(chars, 9), + GetOrNull(chars, 10), + GetOrNull(chars, 11), + GetOrNull(chars, 12), + GetOrNull(chars, 13), + GetOrNull(chars, 14)} {} + + // Disable sanitizer as we must always be able to read `tag`. + ABSL_CORD_INTERNAL_NO_SANITIZE + int8_t tag() const { return reinterpret_cast(this)[0]; } + void set_tag(int8_t rhs) { reinterpret_cast(this)[0] = rhs; } + + char* as_chars() { return data + 1; } + const char* as_chars() const { return data + 1; } + + bool is_tree() const { return (tag() & 1) != 0; } + + size_t inline_size() const { + ABSL_ASSERT(!is_tree()); + return static_cast(tag()) >> 1; + } + + void set_inline_size(size_t size) { + ABSL_ASSERT(size <= kMaxInline); + set_tag(static_cast(size << 1)); + } + + CordRep* tree() const { return as_tree.rep; } + void set_tree(CordRep* rhs) { as_tree.rep = rhs; } + + cordz_info_t cordz_info() const { return as_tree.cordz_info; } + void set_cordz_info(cordz_info_t rhs) { as_tree.cordz_info = rhs; } + + void make_tree(CordRep* tree) { + as_tree.rep = tree; + as_tree.cordz_info = kNullCordzInfo; + } + +#ifdef ABSL_INTERNAL_CORD_HAVE_SANITIZER + constexpr Rep SanitizerSafeCopy() const { + if (!absl::is_constant_evaluated()) { + Rep res; + if (is_tree()) { + res = *this; + } else { + res.set_tag(tag()); + memcpy(res.as_chars(), as_chars(), inline_size()); + } + return res; + } else { + return *this; + } + } +#else + constexpr const Rep& SanitizerSafeCopy() const { return *this; } +#endif + + // If the data has length <= kMaxInline, we store it in `data`, and + // store the size in the first char of `data` shifted left + 1. + // Else we store it in a tree and store a pointer to that tree in + // `as_tree.rep` with a tagged pointer to make `tag() & 1` non zero. + union { + char data[kMaxInline + 1]; + AsTree as_tree; + }; + }; + + // Private implementation of `Compare()` + static inline int Compare(const Rep& lhs, const Rep& rhs) { + uint64_t x, y; + memcpy(&x, lhs.as_chars(), sizeof(x)); + memcpy(&y, rhs.as_chars(), sizeof(y)); + if (x == y) { + memcpy(&x, lhs.as_chars() + 7, sizeof(x)); + memcpy(&y, rhs.as_chars() + 7, sizeof(y)); + if (x == y) { + if (lhs.inline_size() == rhs.inline_size()) return 0; + return lhs.inline_size() < rhs.inline_size() ? -1 : 1; + } + } + x = absl::big_endian::FromHost64(x); + y = absl::big_endian::FromHost64(y); + return x < y ? -1 : 1; + } + + Rep rep_; +}; + +static_assert(sizeof(InlineData) == kMaxInline + 1, ""); + +#ifdef ABSL_INTERNAL_CORD_HAVE_SANITIZER + +constexpr InlineData::InlineData(const InlineData& rhs) noexcept + : rep_(rhs.rep_.SanitizerSafeCopy()) { + poison(); +} + +inline InlineData& InlineData::operator=(const InlineData& rhs) noexcept { + unpoison(); + rep_ = rhs.rep_.SanitizerSafeCopy(); + poison(); + return *this; +} + +constexpr void InlineData::poison_this() { + if (!absl::is_constant_evaluated()) { + container_internal::SanitizerPoisonObject(this); + } +} + +constexpr void InlineData::unpoison() { + if (!absl::is_constant_evaluated()) { + container_internal::SanitizerUnpoisonObject(this); + } +} + +constexpr void InlineData::poison() { + if (!absl::is_constant_evaluated()) { + if (is_tree()) { + container_internal::SanitizerUnpoisonObject(this); + } else if (const size_t size = inline_size()) { + if (size < kMaxInline) { + const char* end = rep_.as_chars() + size; + container_internal::SanitizerPoisonMemoryRegion(end, kMaxInline - size); + } + } else { + container_internal::SanitizerPoisonObject(this); + } + } +} + +#else // ABSL_INTERNAL_CORD_HAVE_SANITIZER + +constexpr InlineData::InlineData(const InlineData&) noexcept = default; +inline InlineData& InlineData::operator=(const InlineData&) noexcept = default; + +constexpr void InlineData::poison_this() {} +constexpr void InlineData::unpoison() {} +constexpr void InlineData::poison() {} + +#endif // ABSL_INTERNAL_CORD_HAVE_SANITIZER + +inline CordRepSubstring* CordRep::substring() { + assert(IsSubstring()); + return static_cast(this); +} + +inline const CordRepSubstring* CordRep::substring() const { + assert(IsSubstring()); + return static_cast(this); +} + +inline CordRepExternal* CordRep::external() { + assert(IsExternal()); + return static_cast(this); +} + +inline const CordRepExternal* CordRep::external() const { + assert(IsExternal()); + return static_cast(this); +} + +inline CordRep* CordRep::Ref(CordRep* rep) { + // ABSL_ASSUME is a workaround for + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105585 + ABSL_ASSUME(rep != nullptr); + rep->refcount.Increment(); + return rep; +} + +inline void CordRep::Unref(CordRep* rep) { + assert(rep != nullptr); + // Expect refcount to be 0. Avoiding the cost of an atomic decrement should + // typically outweigh the cost of an extra branch checking for ref == 1. + if (ABSL_PREDICT_FALSE(!rep->refcount.DecrementExpectHighRefcount())) { + Destroy(rep); + } +} + } // namespace cord_internal + ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_STRINGS_INTERNAL_CORD_INTERNAL_H_ diff --git a/Siv3D/include/ThirdParty/absl/strings/internal/cord_rep_btree.h b/Siv3D/include/ThirdParty/absl/strings/internal/cord_rep_btree.h new file mode 100644 index 0000000000..be94b62ec8 --- /dev/null +++ b/Siv3D/include/ThirdParty/absl/strings/internal/cord_rep_btree.h @@ -0,0 +1,944 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_INTERNAL_CORD_REP_BTREE_H_ +#define ABSL_STRINGS_INTERNAL_CORD_REP_BTREE_H_ + +#include +#include +#include + +#include "absl/base/config.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/base/optimization.h" +#include "absl/strings/internal/cord_data_edge.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_flat.h" +#include "absl/strings/string_view.h" +#include "absl/types/span.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// `SetCordBtreeExhaustiveValidation()` can be set to force exhaustive +// validation in debug assertions, and code that calls `IsValid()` +// explicitly. By default, assertions should be relatively cheap and +// AssertValid() can easily lead to O(n^2) complexity as recursive / full tree +// validation is O(n). +void SetCordBtreeExhaustiveValidation(bool do_exaustive_validation); +bool IsCordBtreeExhaustiveValidationEnabled(); + +class CordRepBtreeNavigator; + +// CordRepBtree is as the name implies a btree implementation of a Cordrep tree. +// Data is stored at the leaf level only, non leaf nodes contain down pointers +// only. Allowed types of data edges are FLAT, EXTERNAL and SUBSTRINGs of FLAT +// or EXTERNAL nodes. The implementation allows for data to be added to either +// end of the tree only, it does not provide any 'insert' logic. This has the +// benefit that we can expect good fill ratios: all nodes except the outer +// 'legs' will have 100% fill ratios for trees built using Append/Prepend +// methods. Merged trees will typically have a fill ratio well above 50% as in a +// similar fashion, one side of the merged tree will typically have a 100% fill +// ratio, and the 'open' end will average 50%. All operations are O(log(n)) or +// better, and the tree never needs balancing. +// +// All methods accepting a CordRep* or CordRepBtree* adopt a reference on that +// input unless explicitly stated otherwise. All functions returning a CordRep* +// or CordRepBtree* instance transfer a reference back to the caller. +// Simplified, callers both 'donate' and 'consume' a reference count on each +// call, simplifying the API. An example of building a tree: +// +// CordRepBtree* tree = CordRepBtree::Create(MakeFlat("Hello")); +// tree = CordRepBtree::Append(tree, MakeFlat("world")); +// +// In the above example, all inputs are consumed, making each call affecting +// `tree` reference count neutral. The returned `tree` value can be different +// from the input if the input is shared with other threads, or if the tree +// grows in height, but callers typically never have to concern themselves with +// that and trust that all methods DTRT at all times. +class CordRepBtree : public CordRep { + public: + // EdgeType identifies `front` and `back` enum values. + // Various implementations in CordRepBtree such as `Add` and `Edge` are + // generic and templated on operating on either of the boundary edges. + // For more information on the possible edges contained in a CordRepBtree + // instance see the documentation for `edges_`. + enum class EdgeType { kFront, kBack }; + + // Convenience constants into `EdgeType` + static constexpr EdgeType kFront = EdgeType::kFront; + static constexpr EdgeType kBack = EdgeType::kBack; + + // Maximum number of edges: based on experiments and performance data, we can + // pick suitable values resulting in optimum cacheline aligned values. The + // preferred values are based on 64-bit systems where we aim to align this + // class onto 64 bytes, i.e.: 6 = 64 bytes, 14 = 128 bytes, etc. + // TODO(b/192061034): experiment with alternative sizes. + static constexpr size_t kMaxCapacity = 6; + + // Reasonable maximum height of the btree. We can expect a fill ratio of at + // least 50%: trees are always expanded at the front or back. Concatenating + // trees will then typically fold at the top most node, where the lower nodes + // are at least at capacity on one side of joined inputs. At a lower fill + // rate of 4 edges per node, we have capacity for ~16 million leaf nodes. + // We will fail / abort if an application ever exceeds this height, which + // should be extremely rare (near impossible) and be an indication of an + // application error: we do not assume it reasonable for any application to + // operate correctly with such monster trees. + // Another compelling reason for the number `12` is that any contextual stack + // required for navigation or insertion requires 12 words and 12 bytes, which + // fits inside 2 cache lines with some room to spare, and is reasonable as a + // local stack variable compared to Cord's current near 400 bytes stack use. + // The maximum `height` value of a node is then `kMaxDepth - 1` as node height + // values start with a value of 0 for leaf nodes. + static constexpr size_t kMaxDepth = 12; + // See comments on height() for why this is an int and not a size_t. + static constexpr int kMaxHeight = static_cast(kMaxDepth - 1); + + // `Action` defines the action for unwinding changes done at the btree's leaf + // level that need to be propagated up to the parent node(s). Each operation + // on a node has an effect / action defined as follows: + // - kSelf + // The operation (add / update, etc) was performed directly on the node as + // the node is private to the current thread (i.e.: not shared directly or + // indirectly through a refcount > 1). Changes can be propagated directly to + // all parent nodes as all parent nodes are also then private to the current + // thread. + // - kCopied + // The operation (add / update, etc) was performed on a copy of the original + // node, as the node is (potentially) directly or indirectly shared with + // other threads. Changes need to be propagated into the parent nodes where + // the old down pointer must be unreffed and replaced with this new copy. + // Such changes to parent nodes may themselves require a copy if the parent + // node is also shared. A kCopied action can propagate all the way to the + // top node where we then must unref the `tree` input provided by the + // caller, and return the new copy. + // - kPopped + // The operation (typically add) could not be satisfied due to insufficient + // capacity in the targeted node, and a new 'leg' was created that needs to + // be added into the parent node. For example, adding a FLAT inside a leaf + // node that is at capacity will create a new leaf node containing that + // FLAT, that needs to be 'popped' up the btree. Such 'pop' actions can + // cascade up the tree if parent nodes are also at capacity. A 'Popped' + // action propagating all the way to the top of the tree will result in + // the tree becoming one level higher than the current tree through a final + // `CordRepBtree::New(tree, popped)` call, resulting in a new top node + // referencing the old tree and the new (fully popped upwards) 'leg'. + enum Action { kSelf, kCopied, kPopped }; + + // Result of an operation on a node. See the `Action` enum for details. + struct OpResult { + CordRepBtree* tree; + Action action; + }; + + // Return value of the CopyPrefix and CopySuffix methods which can + // return a node or data edge at any height inside the tree. + // A height of 0 defines the lowest (leaf) node, a height of -1 identifies + // `edge` as being a plain data node: EXTERNAL / FLAT or SUBSTRING thereof. + struct CopyResult { + CordRep* edge; + int height; + }; + + // Logical position inside a node: + // - index: index of the edge. + // - n: size or offset value depending on context. + struct Position { + size_t index; + size_t n; + }; + + // Creates a btree from the given input. Adopts a ref of `rep`. + // If the input `rep` is itself a btree, i.e., `IsBtree()`, then this + // function immediately returns `rep->btree()`. If the input is a valid data + // edge (see IsDataEdge()), then a new leaf node is returned containing `rep` + // as the sole data edge. Else, the input is assumed to be a (legacy) concat + // tree, and the input is consumed and transformed into a btree(). + static CordRepBtree* Create(CordRep* rep); + + // Destroys the provided tree. Should only be called by cord internal API's, + // typically after a ref_count.Decrement() on the last reference count. + static void Destroy(CordRepBtree* tree); + + // Destruction + static void Delete(CordRepBtree* tree) { delete tree; } + + // Use CordRep::Unref() as we overload for absl::Span. + using CordRep::Unref; + + // Unrefs all edges in `edges` which are assumed to be 'likely one'. + static void Unref(absl::Span edges); + + // Appends / Prepends an existing CordRep instance to this tree. + // The below methods accept three types of input: + // 1) `rep` is a data node (See `IsDataNode` for valid data edges). + // `rep` is appended or prepended to this tree 'as is'. + // 2) `rep` is a BTREE. + // `rep` is merged into `tree` respecting the Append/Prepend order. + // 3) `rep` is some other (legacy) type. + // `rep` is converted in place and added to `tree` + // Requires `tree` and `rep` to be not null. + static CordRepBtree* Append(CordRepBtree* tree, CordRep* rep); + static CordRepBtree* Prepend(CordRepBtree* tree, CordRep* rep); + + // Append/Prepend the data in `data` to this tree. + // The `extra` parameter defines how much extra capacity should be allocated + // for any additional FLAT being allocated. This is an optimization hint from + // the caller. For example, a caller may need to add 2 string_views of data + // "abc" and "defghi" which are not consecutive. The caller can in this case + // invoke `AddData(tree, "abc", 6)`, and any newly added flat is allocated + // where possible with at least 6 bytes of extra capacity beyond `length`. + // This helps avoiding data getting fragmented over multiple flats. + // There is no limit on the size of `data`. If `data` can not be stored inside + // a single flat, then the function will iteratively add flats until all data + // has been consumed and appended or prepended to the tree. + static CordRepBtree* Append(CordRepBtree* tree, string_view data, + size_t extra = 0); + static CordRepBtree* Prepend(CordRepBtree* tree, string_view data, + size_t extra = 0); + + // Returns a new tree, containing `n` bytes of data from this instance + // starting at offset `offset`. Where possible, the returned tree shares + // (re-uses) data edges and nodes with this instance to minimize the + // combined memory footprint of both trees. + // Requires `offset + n <= length`. Returns `nullptr` if `n` is zero. + CordRep* SubTree(size_t offset, size_t n); + + // Removes `n` trailing bytes from `tree`, and returns the resulting tree + // or data edge. Returns `tree` if n is zero, and nullptr if n == length. + // This function is logically identical to: + // result = tree->SubTree(0, tree->length - n); + // Unref(tree); + // return result; + // However, the actual implementation will as much as possible perform 'in + // place' modifications on the tree on all nodes and edges that are mutable. + // For example, in a fully privately owned tree with the last edge being a + // flat of length 12, RemoveSuffix(1) will simply set the length of that data + // edge to 11, and reduce the length of all nodes on the edge path by 1. + static CordRep* RemoveSuffix(CordRepBtree* tree, size_t n); + + // Returns the character at the given offset. + char GetCharacter(size_t offset) const; + + // Returns true if this node holds a single data edge, and if so, sets + // `fragment` to reference the contained data. `fragment` is an optional + // output parameter and allowed to be null. + bool IsFlat(absl::string_view* fragment) const; + + // Returns true if the data of `n` bytes starting at offset `offset` + // is contained in a single data edge, and if so, sets fragment to reference + // the contained data. `fragment` is an optional output parameter and allowed + // to be null. + bool IsFlat(size_t offset, size_t n, absl::string_view* fragment) const; + + // Returns a span (mutable range of bytes) of up to `size` bytes into the + // last FLAT data edge inside this tree under the following conditions: + // - none of the nodes down into the FLAT node are shared. + // - the last data edge in this tree is a non-shared FLAT. + // - the referenced FLAT has additional capacity available. + // If all these conditions are met, a non-empty span is returned, and the + // length of the flat node and involved tree nodes have been increased by + // `span.length()`. The caller is responsible for immediately assigning values + // to all uninitialized data reference by the returned span. + // Requires `this->refcount.IsOne()`: this function forces the caller to do + // this fast path check on the top level node, as this is the most commonly + // shared node of a cord tree. + Span GetAppendBuffer(size_t size); + + // Extracts the right-most data edge from this tree iff: + // - the tree and all internal edges to the right-most node are not shared. + // - the right-most node is a FLAT node and not shared. + // - the right-most node has at least the desired extra capacity. + // + // Returns {tree, nullptr} if any of the above conditions are not met. + // This method effectively removes data from the tree. The intent of this + // method is to allow applications appending small string data to use + // pre-existing capacity, and add the modified rep back to the tree. + // + // Simplified such code would look similar to this: + // void MyTreeBuilder::Append(string_view data) { + // ExtractResult result = CordRepBtree::ExtractAppendBuffer(tree_, 1); + // if (CordRep* rep = result.extracted) { + // size_t available = rep->Capacity() - rep->length; + // size_t n = std::min(data.size(), n); + // memcpy(rep->Data(), data.data(), n); + // rep->length += n; + // data.remove_prefix(n); + // if (!result.tree->IsBtree()) { + // tree_ = CordRepBtree::Create(result.tree); + // } + // tree_ = CordRepBtree::Append(tree_, rep); + // } + // ... + // // Remaining edge in `result.tree`. + // } + static ExtractResult ExtractAppendBuffer(CordRepBtree* tree, + size_t extra_capacity = 1); + + // Returns the `height` of the tree. The height of a tree is limited to + // kMaxHeight. `height` is implemented as an `int` as in some places we + // use negative (-1) values for 'data edges'. + int height() const { return static_cast(storage[0]); } + + // Properties: begin, back, end, front/back boundary indexes. + size_t begin() const { return static_cast(storage[1]); } + size_t back() const { return static_cast(storage[2]) - 1; } + size_t end() const { return static_cast(storage[2]); } + size_t index(EdgeType edge) const { + return edge == kFront ? begin() : back(); + } + + // Properties: size and capacity. + // `capacity` contains the current capacity of this instance, where + // `kMaxCapacity` contains the maximum capacity of a btree node. + // For now, `capacity` and `kMaxCapacity` return the same value, but this may + // change in the future if we see benefit in dynamically sizing 'small' nodes + // to 'large' nodes for large data trees. + size_t size() const { return end() - begin(); } + size_t capacity() const { return kMaxCapacity; } + + // Edge access + inline CordRep* Edge(size_t index) const; + inline CordRep* Edge(EdgeType edge_type) const; + inline absl::Span Edges() const; + inline absl::Span Edges(size_t begin, size_t end) const; + + // Returns reference to the data edge at `index`. + // Requires this instance to be a leaf node, and `index` to be valid index. + inline absl::string_view Data(size_t index) const; + + // Diagnostics: returns true if `tree` is valid and internally consistent. + // If `shallow` is false, then the provided top level node and all child nodes + // below it are recursively checked. If `shallow` is true, only the provided + // node in `tree` and the cumulative length, type and height of the direct + // child nodes of `tree` are checked. The value of `shallow` is ignored if the + // internal `cord_btree_exhaustive_validation` diagnostics variable is true, + // in which case the performed validations works as if `shallow` were false. + // This function is intended for debugging and testing purposes only. + static bool IsValid(const CordRepBtree* tree, bool shallow = false); + + // Diagnostics: asserts that the provided tree is valid. + // `AssertValid()` performs a shallow validation by default. `shallow` can be + // set to false in which case an exhaustive validation is performed. This + // function is implemented in terms of calling `IsValid()` and asserting the + // return value to be true. See `IsValid()` for more information. + // This function is intended for debugging and testing purposes only. + static CordRepBtree* AssertValid(CordRepBtree* tree, bool shallow = true); + static const CordRepBtree* AssertValid(const CordRepBtree* tree, + bool shallow = true); + + // Diagnostics: dump the contents of this tree to `stream`. + // This function is intended for debugging and testing purposes only. + static void Dump(const CordRep* rep, std::ostream& stream); + static void Dump(const CordRep* rep, absl::string_view label, + std::ostream& stream); + static void Dump(const CordRep* rep, absl::string_view label, + bool include_contents, std::ostream& stream); + + // Adds the edge `edge` to this node if possible. `owned` indicates if the + // current node is potentially shared or not with other threads. Returns: + // - {kSelf, } + // The edge was directly added to this node. + // - {kCopied, } + // The edge was added to a copy of this node. + // - {kPopped, New(edge, height())} + // A new leg with the edge was created as this node has no extra capacity. + template + inline OpResult AddEdge(bool owned, CordRep* edge, size_t delta); + + // Replaces the front or back edge with the provided new edge. Returns: + // - {kSelf, } + // The edge was directly set in this node. The old edge is unreffed. + // - {kCopied, } + // A copy of this node was created with the new edge value. + // In both cases, the function adopts a reference on `edge`. + template + OpResult SetEdge(bool owned, CordRep* edge, size_t delta); + + // Creates a new empty node at the specified height. + static CordRepBtree* New(int height = 0); + + // Creates a new node containing `rep`, with the height being computed + // automatically based on the type of `rep`. + static CordRepBtree* New(CordRep* rep); + + // Creates a new node containing both `front` and `back` at height + // `front.height() + 1`. Requires `back.height() == front.height()`. + static CordRepBtree* New(CordRepBtree* front, CordRepBtree* back); + + // Creates a fully balanced tree from the provided tree by rebuilding a new + // tree from all data edges in the input. This function is automatically + // invoked internally when the tree exceeds the maximum height. + static CordRepBtree* Rebuild(CordRepBtree* tree); + + private: + CordRepBtree() = default; + ~CordRepBtree() = default; + + // Initializes the main properties `tag`, `begin`, `end`, `height`. + inline void InitInstance(int height, size_t begin = 0, size_t end = 0); + + // Direct property access begin / end + void set_begin(size_t begin) { storage[1] = static_cast(begin); } + void set_end(size_t end) { storage[2] = static_cast(end); } + + // Decreases the value of `begin` by `n`, and returns the new value. Notice + // how this returns the new value unlike atomic::fetch_add which returns the + // old value. This is because this is used to prepend edges at 'begin - 1'. + size_t sub_fetch_begin(size_t n) { + storage[1] -= static_cast(n); + return storage[1]; + } + + // Increases the value of `end` by `n`, and returns the previous value. This + // function is typically used to append edges at 'end'. + size_t fetch_add_end(size_t n) { + const uint8_t current = storage[2]; + storage[2] = static_cast(current + n); + return current; + } + + // Returns the index of the last edge starting on, or before `offset`, with + // `n` containing the relative offset of `offset` inside that edge. + // Requires `offset` < length. + Position IndexOf(size_t offset) const; + + // Returns the index of the last edge starting before `offset`, with `n` + // containing the relative offset of `offset` inside that edge. + // This function is useful to find the edges for some span of bytes ending at + // `offset` (i.e., `n` bytes). For example: + // + // Position pos = IndexBefore(n) + // edges = Edges(begin(), pos.index) // All full edges (may be empty) + // last = Sub(Edge(pos.index), 0, pos.n) // Last partial edge (may be empty) + // + // Requires 0 < `offset` <= length. + Position IndexBefore(size_t offset) const; + + // Returns the index of the edge ending at (or on) length `length`, and the + // number of bytes inside that edge up to `length`. For example, if we have a + // Node with 2 edges, one of 10 and one of 20 long, then IndexOfLength(27) + // will return {1, 17}, and IndexOfLength(10) will return {0, 10}. + Position IndexOfLength(size_t n) const; + + // Identical to the above function except starting from the position `front`. + // This function is equivalent to `IndexBefore(front.n + offset)`, with + // the difference that this function is optimized to start at `front.index`. + Position IndexBefore(Position front, size_t offset) const; + + // Returns the index of the edge directly beyond the edge containing offset + // `offset`, with `n` containing the distance of that edge from `offset`. + // This function is useful for iteratively finding suffix nodes and remaining + // partial bytes in left-most suffix nodes as for example in CopySuffix. + // Requires `offset` < length. + Position IndexBeyond(size_t offset) const; + + // Creates a new leaf node containing as much data as possible from `data`. + // The data is added either forwards or reversed depending on `edge_type`. + // Callers must check the length of the returned node to determine if all data + // was copied or not. + // See the `Append/Prepend` function for the meaning and purpose of `extra`. + template + static CordRepBtree* NewLeaf(absl::string_view data, size_t extra); + + // Creates a raw copy of this Btree node with the specified length, copying + // all properties, but without adding any references to existing edges. + CordRepBtree* CopyRaw(size_t new_length) const; + + // Creates a full copy of this Btree node, adding a reference on all edges. + CordRepBtree* Copy() const; + + // Creates a partial copy of this Btree node, copying all edges up to `end`, + // adding a reference on each copied edge, and sets the length of the newly + // created copy to `new_length`. + CordRepBtree* CopyBeginTo(size_t end, size_t new_length) const; + + // Returns a tree containing the edges [tree->begin(), end) and length + // of `new_length`. This method consumes a reference on the provided + // tree, and logically performs the following operation: + // result = tree->CopyBeginTo(end, new_length); + // CordRep::Unref(tree); + // return result; + static CordRepBtree* ConsumeBeginTo(CordRepBtree* tree, size_t end, + size_t new_length); + + // Creates a partial copy of this Btree node, copying all edges starting at + // `begin`, adding a reference on each copied edge, and sets the length of + // the newly created copy to `new_length`. + CordRepBtree* CopyToEndFrom(size_t begin, size_t new_length) const; + + // Extracts and returns the front edge from the provided tree. + // This method consumes a reference on the provided tree, and logically + // performs the following operation: + // edge = CordRep::Ref(tree->Edge(kFront)); + // CordRep::Unref(tree); + // return edge; + static CordRep* ExtractFront(CordRepBtree* tree); + + // Returns a tree containing the result of appending `right` to `left`. + static CordRepBtree* MergeTrees(CordRepBtree* left, CordRepBtree* right); + + // Fallback functions for `Create()`, `Append()` and `Prepend()` which + // deal with legacy / non conforming input, i.e.: CONCAT trees. + static CordRepBtree* CreateSlow(CordRep* rep); + static CordRepBtree* AppendSlow(CordRepBtree*, CordRep* rep); + static CordRepBtree* PrependSlow(CordRepBtree*, CordRep* rep); + + // Recursively rebuilds `tree` into `stack`. If 'consume` is set to true, the + // function will consume a reference on `tree`. `stack` is a null terminated + // array containing the new tree's state, with the current leaf node at + // stack[0], and parent nodes above that, or null for 'top of tree'. + static void Rebuild(CordRepBtree** stack, CordRepBtree* tree, bool consume); + + // Aligns existing edges to start at index 0, to allow for a new edge to be + // added to the back of the current edges. + inline void AlignBegin(); + + // Aligns existing edges to end at `capacity`, to allow for a new edge to be + // added in front of the current edges. + inline void AlignEnd(); + + // Adds the provided edge to this node. + // Requires this node to have capacity for the edge. Realigns / moves + // existing edges as needed to prepend or append the new edge. + template + inline void Add(CordRep* rep); + + // Adds the provided edges to this node. + // Requires this node to have capacity for the edges. Realigns / moves + // existing edges as needed to prepend or append the new edges. + template + inline void Add(absl::Span); + + // Adds data from `data` to this node until either all data has been consumed, + // or there is no more capacity for additional flat nodes inside this node. + // Requires the current node to be a leaf node, data to be non empty, and the + // current node to have capacity for at least one more data edge. + // Returns any remaining data from `data` that was not added, which is + // depending on the edge type (front / back) either the remaining prefix of + // suffix of the input. + // See the `Append/Prepend` function for the meaning and purpose of `extra`. + template + absl::string_view AddData(absl::string_view data, size_t extra); + + // Replace the front or back edge with the provided value. + // Adopts a reference on `edge` and unrefs the old edge. + template + inline void SetEdge(CordRep* edge); + + // Returns a partial copy of the current tree containing the first `n` bytes + // of data. `CopyResult` contains both the resulting edge and its height. The + // resulting tree may be less high than the current tree, or even be a single + // matching data edge if `allow_folding` is set to true. + // For example, if `n == 1`, then the result will be the single data edge, and + // height will be set to -1 (one below the owning leaf node). If n == 0, this + // function returns null. Requires `n <= length` + CopyResult CopyPrefix(size_t n, bool allow_folding = true); + + // Returns a partial copy of the current tree containing all data starting + // after `offset`. `CopyResult` contains both the resulting edge and its + // height. The resulting tree may be less high than the current tree, or even + // be a single matching data edge. For example, if `n == length - 1`, then the + // result will be a single data edge, and height will be set to -1 (one below + // the owning leaf node). + // Requires `offset < length` + CopyResult CopySuffix(size_t offset); + + // Returns a OpResult value of {this, kSelf} or {Copy(), kCopied} + // depending on the value of `owned`. + inline OpResult ToOpResult(bool owned); + + // Adds `rep` to the specified tree, returning the modified tree. + template + static CordRepBtree* AddCordRep(CordRepBtree* tree, CordRep* rep); + + // Adds `data` to the specified tree, returning the modified tree. + // See the `Append/Prepend` function for the meaning and purpose of `extra`. + template + static CordRepBtree* AddData(CordRepBtree* tree, absl::string_view data, + size_t extra = 0); + + // Merges `src` into `dst` with `src` being added either before (kFront) or + // after (kBack) `dst`. Requires the height of `dst` to be greater than or + // equal to the height of `src`. + template + static CordRepBtree* Merge(CordRepBtree* dst, CordRepBtree* src); + + // Fallback version of GetAppendBuffer for large trees: GetAppendBuffer() + // implements an inlined version for trees of limited height (3 levels), + // GetAppendBufferSlow implements the logic for large trees. + Span GetAppendBufferSlow(size_t size); + + // `edges_` contains all edges starting from this instance. + // These are explicitly `child` edges only, a cord btree (or any cord tree in + // that respect) does not store `parent` pointers anywhere: multiple trees / + // parents can reference the same shared child edge. The type of these edges + // depends on the height of the node. `Leaf nodes` (height == 0) contain `data + // edges` (external or flat nodes, or sub-strings thereof). All other nodes + // (height > 0) contain pointers to BTREE nodes with a height of `height - 1`. + CordRep* edges_[kMaxCapacity]; + + friend class CordRepBtreeTestPeer; + friend class CordRepBtreeNavigator; +}; + +inline CordRepBtree* CordRep::btree() { + assert(IsBtree()); + return static_cast(this); +} + +inline const CordRepBtree* CordRep::btree() const { + assert(IsBtree()); + return static_cast(this); +} + +inline void CordRepBtree::InitInstance(int height, size_t begin, size_t end) { + tag = BTREE; + storage[0] = static_cast(height); + storage[1] = static_cast(begin); + storage[2] = static_cast(end); +} + +inline CordRep* CordRepBtree::Edge(size_t index) const { + assert(index >= begin()); + assert(index < end()); + return edges_[index]; +} + +inline CordRep* CordRepBtree::Edge(EdgeType edge_type) const { + return edges_[edge_type == kFront ? begin() : back()]; +} + +inline absl::Span CordRepBtree::Edges() const { + return {edges_ + begin(), size()}; +} + +inline absl::Span CordRepBtree::Edges(size_t begin, + size_t end) const { + assert(begin <= end); + assert(begin >= this->begin()); + assert(end <= this->end()); + return {edges_ + begin, static_cast(end - begin)}; +} + +inline absl::string_view CordRepBtree::Data(size_t index) const { + assert(height() == 0); + return EdgeData(Edge(index)); +} + +inline CordRepBtree* CordRepBtree::New(int height) { + CordRepBtree* tree = new CordRepBtree; + tree->length = 0; + tree->InitInstance(height); + return tree; +} + +inline CordRepBtree* CordRepBtree::New(CordRep* rep) { + CordRepBtree* tree = new CordRepBtree; + int height = rep->IsBtree() ? rep->btree()->height() + 1 : 0; + tree->length = rep->length; + tree->InitInstance(height, /*begin=*/0, /*end=*/1); + tree->edges_[0] = rep; + return tree; +} + +inline CordRepBtree* CordRepBtree::New(CordRepBtree* front, + CordRepBtree* back) { + assert(front->height() == back->height()); + CordRepBtree* tree = new CordRepBtree; + tree->length = front->length + back->length; + tree->InitInstance(front->height() + 1, /*begin=*/0, /*end=*/2); + tree->edges_[0] = front; + tree->edges_[1] = back; + return tree; +} + +inline void CordRepBtree::Unref(absl::Span edges) { + for (CordRep* edge : edges) { + if (ABSL_PREDICT_FALSE(!edge->refcount.Decrement())) { + CordRep::Destroy(edge); + } + } +} + +inline CordRepBtree* CordRepBtree::CopyRaw(size_t new_length) const { + CordRepBtree* tree = new CordRepBtree; + + // `length` and `refcount` are the first members of `CordRepBtree`. + // We initialize `length` using the given length, have `refcount` be set to + // ref = 1 through its default constructor, and copy all data beyond + // 'refcount' which starts with `tag` using a single memcpy: all contents + // except `refcount` is trivially copyable, and the compiler does not + // efficiently coalesce member-wise copy of these members. + // See https://gcc.godbolt.org/z/qY8zsca6z + // # LINT.IfChange(copy_raw) + tree->length = new_length; + uint8_t* dst = &tree->tag; + const uint8_t* src = &tag; + const ptrdiff_t offset = src - reinterpret_cast(this); + memcpy(dst, src, sizeof(CordRepBtree) - static_cast(offset)); + return tree; + // # LINT.ThenChange() +} + +inline CordRepBtree* CordRepBtree::Copy() const { + CordRepBtree* tree = CopyRaw(length); + for (CordRep* rep : Edges()) CordRep::Ref(rep); + return tree; +} + +inline CordRepBtree* CordRepBtree::CopyToEndFrom(size_t begin, + size_t new_length) const { + assert(begin >= this->begin()); + assert(begin <= this->end()); + CordRepBtree* tree = CopyRaw(new_length); + tree->set_begin(begin); + for (CordRep* edge : tree->Edges()) CordRep::Ref(edge); + return tree; +} + +inline CordRepBtree* CordRepBtree::CopyBeginTo(size_t end, + size_t new_length) const { + assert(end <= capacity()); + assert(end >= this->begin()); + CordRepBtree* tree = CopyRaw(new_length); + tree->set_end(end); + for (CordRep* edge : tree->Edges()) CordRep::Ref(edge); + return tree; +} + +inline void CordRepBtree::AlignBegin() { + // The below code itself does not need to be fast as typically we have + // mono-directional append/prepend calls, and `begin` / `end` are typically + // adjusted no more than once. But we want to avoid potential register clobber + // effects, making the compiler emit register save/store/spills, and minimize + // the size of code. + const size_t delta = begin(); + if (ABSL_PREDICT_FALSE(delta != 0)) { + const size_t new_end = end() - delta; + set_begin(0); + set_end(new_end); + // TODO(mvels): we can write this using 2 loads / 2 stores depending on + // total size for the kMaxCapacity = 6 case. I.e., we can branch (switch) on + // size, and then do overlapping load/store of up to 4 pointers (inlined as + // XMM, YMM or ZMM load/store) and up to 2 pointers (XMM / YMM), which is a) + // compact and b) not clobbering any registers. + ABSL_ASSUME(new_end <= kMaxCapacity); +#ifdef __clang__ +#pragma unroll 1 +#endif + for (size_t i = 0; i < new_end; ++i) { + edges_[i] = edges_[i + delta]; + } + } +} + +inline void CordRepBtree::AlignEnd() { + // See comments in `AlignBegin` for motivation on the hand-rolled for loops. + const size_t delta = capacity() - end(); + if (delta != 0) { + const size_t new_begin = begin() + delta; + const size_t new_end = end() + delta; + set_begin(new_begin); + set_end(new_end); + ABSL_ASSUME(new_end <= kMaxCapacity); +#ifdef __clang__ +#pragma unroll 1 +#endif + for (size_t i = new_end - 1; i >= new_begin; --i) { + edges_[i] = edges_[i - delta]; + } + } +} + +template <> +inline void CordRepBtree::Add(CordRep* rep) { + AlignBegin(); + edges_[fetch_add_end(1)] = rep; +} + +template <> +inline void CordRepBtree::Add( + absl::Span edges) { + AlignBegin(); + size_t new_end = end(); + for (CordRep* edge : edges) edges_[new_end++] = edge; + set_end(new_end); +} + +template <> +inline void CordRepBtree::Add(CordRep* rep) { + AlignEnd(); + edges_[sub_fetch_begin(1)] = rep; +} + +template <> +inline void CordRepBtree::Add( + absl::Span edges) { + AlignEnd(); + size_t new_begin = begin() - edges.size(); + set_begin(new_begin); + for (CordRep* edge : edges) edges_[new_begin++] = edge; +} + +template +inline void CordRepBtree::SetEdge(CordRep* edge) { + const int idx = edge_type == kFront ? begin() : back(); + CordRep::Unref(edges_[idx]); + edges_[idx] = edge; +} + +inline CordRepBtree::OpResult CordRepBtree::ToOpResult(bool owned) { + return owned ? OpResult{this, kSelf} : OpResult{Copy(), kCopied}; +} + +inline CordRepBtree::Position CordRepBtree::IndexOf(size_t offset) const { + assert(offset < length); + size_t index = begin(); + while (offset >= edges_[index]->length) offset -= edges_[index++]->length; + return {index, offset}; +} + +inline CordRepBtree::Position CordRepBtree::IndexBefore(size_t offset) const { + assert(offset > 0); + assert(offset <= length); + size_t index = begin(); + while (offset > edges_[index]->length) offset -= edges_[index++]->length; + return {index, offset}; +} + +inline CordRepBtree::Position CordRepBtree::IndexBefore(Position front, + size_t offset) const { + size_t index = front.index; + offset = offset + front.n; + while (offset > edges_[index]->length) offset -= edges_[index++]->length; + return {index, offset}; +} + +inline CordRepBtree::Position CordRepBtree::IndexOfLength(size_t n) const { + assert(n <= length); + size_t index = back(); + size_t strip = length - n; + while (strip >= edges_[index]->length) strip -= edges_[index--]->length; + return {index, edges_[index]->length - strip}; +} + +inline CordRepBtree::Position CordRepBtree::IndexBeyond( + const size_t offset) const { + // We need to find the edge which `starting offset` is beyond (>=)`offset`. + // For this we can't use the `offset -= length` logic of IndexOf. Instead, we + // track the offset of the `current edge` in `off`, which we increase as we + // iterate over the edges until we find the matching edge. + size_t off = 0; + size_t index = begin(); + while (offset > off) off += edges_[index++]->length; + return {index, off - offset}; +} + +inline CordRepBtree* CordRepBtree::Create(CordRep* rep) { + if (IsDataEdge(rep)) return New(rep); + return CreateSlow(rep); +} + +inline Span CordRepBtree::GetAppendBuffer(size_t size) { + assert(refcount.IsOne()); + CordRepBtree* tree = this; + const int height = this->height(); + CordRepBtree* n1 = tree; + CordRepBtree* n2 = tree; + CordRepBtree* n3 = tree; + switch (height) { + case 3: + tree = tree->Edge(kBack)->btree(); + if (!tree->refcount.IsOne()) return {}; + n2 = tree; + ABSL_FALLTHROUGH_INTENDED; + case 2: + tree = tree->Edge(kBack)->btree(); + if (!tree->refcount.IsOne()) return {}; + n1 = tree; + ABSL_FALLTHROUGH_INTENDED; + case 1: + tree = tree->Edge(kBack)->btree(); + if (!tree->refcount.IsOne()) return {}; + ABSL_FALLTHROUGH_INTENDED; + case 0: + CordRep* edge = tree->Edge(kBack); + if (!edge->refcount.IsOne()) return {}; + if (edge->tag < FLAT) return {}; + size_t avail = edge->flat()->Capacity() - edge->length; + if (avail == 0) return {}; + size_t delta = (std::min)(size, avail); + Span span = {edge->flat()->Data() + edge->length, delta}; + edge->length += delta; + switch (height) { + case 3: + n3->length += delta; + ABSL_FALLTHROUGH_INTENDED; + case 2: + n2->length += delta; + ABSL_FALLTHROUGH_INTENDED; + case 1: + n1->length += delta; + ABSL_FALLTHROUGH_INTENDED; + case 0: + tree->length += delta; + return span; + } + break; + } + return GetAppendBufferSlow(size); +} + +extern template CordRepBtree* CordRepBtree::AddCordRep( + CordRepBtree* tree, CordRep* rep); + +extern template CordRepBtree* CordRepBtree::AddCordRep( + CordRepBtree* tree, CordRep* rep); + +inline CordRepBtree* CordRepBtree::Append(CordRepBtree* tree, CordRep* rep) { + if (ABSL_PREDICT_TRUE(IsDataEdge(rep))) { + return CordRepBtree::AddCordRep(tree, rep); + } + return AppendSlow(tree, rep); +} + +inline CordRepBtree* CordRepBtree::Prepend(CordRepBtree* tree, CordRep* rep) { + if (ABSL_PREDICT_TRUE(IsDataEdge(rep))) { + return CordRepBtree::AddCordRep(tree, rep); + } + return PrependSlow(tree, rep); +} + +#ifdef NDEBUG + +inline CordRepBtree* CordRepBtree::AssertValid(CordRepBtree* tree, + bool /* shallow */) { + return tree; +} + +inline const CordRepBtree* CordRepBtree::AssertValid(const CordRepBtree* tree, + bool /* shallow */) { + return tree; +} + +#endif + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_CORD_REP_BTREE_H_ diff --git a/Siv3D/include/ThirdParty/absl/strings/internal/cord_rep_btree_navigator.h b/Siv3D/include/ThirdParty/absl/strings/internal/cord_rep_btree_navigator.h new file mode 100644 index 0000000000..3d581c877e --- /dev/null +++ b/Siv3D/include/ThirdParty/absl/strings/internal/cord_rep_btree_navigator.h @@ -0,0 +1,267 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_INTERNAL_CORD_REP_BTREE_NAVIGATOR_H_ +#define ABSL_STRINGS_INTERNAL_CORD_REP_BTREE_NAVIGATOR_H_ + +#include +#include + +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_btree.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// CordRepBtreeNavigator is a bi-directional navigator allowing callers to +// navigate all the (leaf) data edges in a CordRepBtree instance. +// +// A CordRepBtreeNavigator instance is by default empty. Callers initialize a +// navigator instance by calling one of `InitFirst()`, `InitLast()` or +// `InitOffset()`, which establishes a current position. Callers can then +// navigate using the `Next`, `Previous`, `Skip` and `Seek` methods. +// +// The navigator instance does not take or adopt a reference on the provided +// `tree` on any of the initialization calls. Callers are responsible for +// guaranteeing the lifecycle of the provided tree. A navigator instance can +// be reset to the empty state by calling `Reset`. +// +// A navigator only keeps positional state on the 'current data edge', it does +// explicitly not keep any 'offset' state. The class does accept and return +// offsets in the `Read()`, `Skip()` and 'Seek()` methods as these would +// otherwise put a big burden on callers. Callers are expected to maintain +// (returned) offset info if they require such granular state. +class CordRepBtreeNavigator { + public: + // The logical position as returned by the Seek() and Skip() functions. + // Returns the current leaf edge for the desired seek or skip position and + // the offset of that position inside that edge. + struct Position { + CordRep* edge; + size_t offset; + }; + + // The read result as returned by the Read() function. + // `tree` contains the resulting tree which is identical to the result + // of calling CordRepBtree::SubTree(...) on the tree being navigated. + // `n` contains the number of bytes used from the last navigated to + // edge of the tree. + struct ReadResult { + CordRep* tree; + size_t n; + }; + + // Returns true if this instance is not empty. + explicit operator bool() const; + + // Returns the tree for this instance or nullptr if empty. + CordRepBtree* btree() const; + + // Returns the data edge of the current position. + // Requires this instance to not be empty. + CordRep* Current() const; + + // Resets this navigator to `tree`, returning the first data edge in the tree. + CordRep* InitFirst(CordRepBtree* tree); + + // Resets this navigator to `tree`, returning the last data edge in the tree. + CordRep* InitLast(CordRepBtree* tree); + + // Resets this navigator to `tree` returning the data edge at position + // `offset` and the relative offset of `offset` into that data edge. + // Returns `Position.edge = nullptr` if the provided offset is greater + // than or equal to the length of the tree, in which case the state of + // the navigator instance remains unchanged. + Position InitOffset(CordRepBtree* tree, size_t offset); + + // Navigates to the next data edge. + // Returns the next data edge or nullptr if there is no next data edge, in + // which case the current position remains unchanged. + CordRep* Next(); + + // Navigates to the previous data edge. + // Returns the previous data edge or nullptr if there is no previous data + // edge, in which case the current position remains unchanged. + CordRep* Previous(); + + // Navigates to the data edge at position `offset`. Returns the navigated to + // data edge in `Position.edge` and the relative offset of `offset` into that + // data edge in `Position.offset`. Returns `Position.edge = nullptr` if the + // provide offset is greater than or equal to the tree's length. + Position Seek(size_t offset); + + // Reads `n` bytes of data starting at offset `edge_offset` of the current + // data edge, and returns the result in `ReadResult.tree`. `ReadResult.n` + // contains the 'bytes used` from the last / current data edge in the tree. + // This allows users that mix regular navigation (using string views) and + // 'read into cord' navigation to keep track of the current state, and which + // bytes have been consumed from a navigator. + // This function returns `ReadResult.tree = nullptr` if the requested length + // exceeds the length of the tree starting at the current data edge. + ReadResult Read(size_t edge_offset, size_t n); + + // Skips `n` bytes forward from the current data edge, returning the navigated + // to data edge in `Position.edge` and `Position.offset` containing the offset + // inside that data edge. Note that the state of the navigator is left + // unchanged if `n` is smaller than the length of the current data edge. + Position Skip(size_t n); + + // Resets this instance to the default / empty state. + void Reset(); + + private: + // Slow path for Next() if Next() reached the end of a leaf node. Backtracks + // up the stack until it finds a node that has a 'next' position available, + // and then does a 'front dive' towards the next leaf node. + CordRep* NextUp(); + + // Slow path for Previous() if Previous() reached the beginning of a leaf + // node. Backtracks up the stack until it finds a node that has a 'previous' + // position available, and then does a 'back dive' towards the previous leaf + // node. + CordRep* PreviousUp(); + + // Generic implementation of InitFirst() and InitLast(). + template + CordRep* Init(CordRepBtree* tree); + + // `height_` contains the height of the current tree, or -1 if empty. + int height_ = -1; + + // `index_` and `node_` contain the navigation state as the 'path' to the + // current data edge which is at `node_[0]->Edge(index_[0])`. The contents + // of these are undefined until the instance is initialized (`height_ >= 0`). + uint8_t index_[CordRepBtree::kMaxDepth]; + CordRepBtree* node_[CordRepBtree::kMaxDepth]; +}; + +// Returns true if this instance is not empty. +inline CordRepBtreeNavigator::operator bool() const { return height_ >= 0; } + +inline CordRepBtree* CordRepBtreeNavigator::btree() const { + return height_ >= 0 ? node_[height_] : nullptr; +} + +inline CordRep* CordRepBtreeNavigator::Current() const { + assert(height_ >= 0); + return node_[0]->Edge(index_[0]); +} + +inline void CordRepBtreeNavigator::Reset() { height_ = -1; } + +inline CordRep* CordRepBtreeNavigator::InitFirst(CordRepBtree* tree) { + return Init(tree); +} + +inline CordRep* CordRepBtreeNavigator::InitLast(CordRepBtree* tree) { + return Init(tree); +} + +template +inline CordRep* CordRepBtreeNavigator::Init(CordRepBtree* tree) { + assert(tree != nullptr); + assert(tree->size() > 0); + assert(tree->height() <= CordRepBtree::kMaxHeight); + int height = height_ = tree->height(); + size_t index = tree->index(edge_type); + node_[height] = tree; + index_[height] = static_cast(index); + while (--height >= 0) { + tree = tree->Edge(index)->btree(); + node_[height] = tree; + index = tree->index(edge_type); + index_[height] = static_cast(index); + } + return node_[0]->Edge(index); +} + +inline CordRepBtreeNavigator::Position CordRepBtreeNavigator::Seek( + size_t offset) { + assert(btree() != nullptr); + int height = height_; + CordRepBtree* edge = node_[height]; + if (ABSL_PREDICT_FALSE(offset >= edge->length)) return {nullptr, 0}; + CordRepBtree::Position index = edge->IndexOf(offset); + index_[height] = static_cast(index.index); + while (--height >= 0) { + edge = edge->Edge(index.index)->btree(); + node_[height] = edge; + index = edge->IndexOf(index.n); + index_[height] = static_cast(index.index); + } + return {edge->Edge(index.index), index.n}; +} + +inline CordRepBtreeNavigator::Position CordRepBtreeNavigator::InitOffset( + CordRepBtree* tree, size_t offset) { + assert(tree != nullptr); + assert(tree->height() <= CordRepBtree::kMaxHeight); + if (ABSL_PREDICT_FALSE(offset >= tree->length)) return {nullptr, 0}; + height_ = tree->height(); + node_[height_] = tree; + return Seek(offset); +} + +inline CordRep* CordRepBtreeNavigator::Next() { + CordRepBtree* edge = node_[0]; + return index_[0] == edge->back() ? NextUp() : edge->Edge(++index_[0]); +} + +inline CordRep* CordRepBtreeNavigator::Previous() { + CordRepBtree* edge = node_[0]; + return index_[0] == edge->begin() ? PreviousUp() : edge->Edge(--index_[0]); +} + +inline CordRep* CordRepBtreeNavigator::NextUp() { + assert(index_[0] == node_[0]->back()); + CordRepBtree* edge; + size_t index; + int height = 0; + do { + if (++height > height_) return nullptr; + edge = node_[height]; + index = index_[height] + 1; + } while (index == edge->end()); + index_[height] = static_cast(index); + do { + node_[--height] = edge = edge->Edge(index)->btree(); + index_[height] = static_cast(index = edge->begin()); + } while (height > 0); + return edge->Edge(index); +} + +inline CordRep* CordRepBtreeNavigator::PreviousUp() { + assert(index_[0] == node_[0]->begin()); + CordRepBtree* edge; + size_t index; + int height = 0; + do { + if (++height > height_) return nullptr; + edge = node_[height]; + index = index_[height]; + } while (index == edge->begin()); + index_[height] = static_cast(--index); + do { + node_[--height] = edge = edge->Edge(index)->btree(); + index_[height] = static_cast(index = edge->back()); + } while (height > 0); + return edge->Edge(index); +} + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_CORD_REP_BTREE_NAVIGATOR_H_ diff --git a/Siv3D/include/ThirdParty/absl/strings/internal/cord_rep_btree_reader.h b/Siv3D/include/ThirdParty/absl/strings/internal/cord_rep_btree_reader.h new file mode 100644 index 0000000000..8db8f8dd17 --- /dev/null +++ b/Siv3D/include/ThirdParty/absl/strings/internal/cord_rep_btree_reader.h @@ -0,0 +1,212 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_INTERNAL_CORD_REP_BTREE_READER_H_ +#define ABSL_STRINGS_INTERNAL_CORD_REP_BTREE_READER_H_ + +#include + +#include "absl/base/config.h" +#include "absl/strings/internal/cord_data_edge.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_btree.h" +#include "absl/strings/internal/cord_rep_btree_navigator.h" +#include "absl/strings/internal/cord_rep_flat.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// CordRepBtreeReader implements logic to iterate over cord btrees. +// References to the underlying data are returned as absl::string_view values. +// The most typical use case is a forward only iteration over tree data. +// The class also provides `Skip()`, `Seek()` and `Read()` methods similar to +// CordRepBtreeNavigator that allow more advanced navigation. +// +// Example: iterate over all data inside a cord btree: +// +// CordRepBtreeReader reader; +// for (string_view sv = reader.Init(tree); !sv.Empty(); sv = sv.Next()) { +// DoSomethingWithDataIn(sv); +// } +// +// All navigation methods always return the next 'chunk' of data. The class +// assumes that all data is directly 'consumed' by the caller. For example: +// invoking `Skip()` will skip the desired number of bytes, and directly +// read and return the next chunk of data directly after the skipped bytes. +// +// Example: iterate over all data inside a btree skipping the first 100 bytes: +// +// CordRepBtreeReader reader; +// absl::string_view sv = reader.Init(tree); +// if (sv.length() > 100) { +// sv.RemovePrefix(100); +// } else { +// sv = reader.Skip(100 - sv.length()); +// } +// while (!sv.empty()) { +// DoSomethingWithDataIn(sv); +// absl::string_view sv = reader.Next(); +// } +// +// It is important to notice that `remaining` is based on the end position of +// the last data edge returned to the caller, not the cumulative data returned +// to the caller which can be less in cases of skipping or seeking over data. +// +// For example, consider a cord btree with five data edges: "abc", "def", "ghi", +// "jkl" and "mno": +// +// absl::string_view sv; +// CordRepBtreeReader reader; +// +// sv = reader.Init(tree); // sv = "abc", remaining = 12 +// sv = reader.Skip(4); // sv = "hi", remaining = 6 +// sv = reader.Skip(2); // sv = "l", remaining = 3 +// sv = reader.Next(); // sv = "mno", remaining = 0 +// sv = reader.Seek(1); // sv = "bc", remaining = 12 +// +class CordRepBtreeReader { + public: + using ReadResult = CordRepBtreeNavigator::ReadResult; + using Position = CordRepBtreeNavigator::Position; + + // Returns true if this instance is not empty. + explicit operator bool() const { return navigator_.btree() != nullptr; } + + // Returns the tree referenced by this instance or nullptr if empty. + CordRepBtree* btree() const { return navigator_.btree(); } + + // Returns the current data edge inside the referenced btree. + // Requires that the current instance is not empty. + CordRep* node() const { return navigator_.Current(); } + + // Returns the length of the referenced tree. + // Requires that the current instance is not empty. + size_t length() const; + + // Returns the number of remaining bytes available for iteration, which is the + // number of bytes directly following the end of the last chunk returned. + // This value will be zero if we iterated over the last edge in the bound + // tree, in which case any call to Next() or Skip() will return an empty + // string_view reflecting the EOF state. + // Note that a call to `Seek()` resets `remaining` to a value based on the + // end position of the chunk returned by that call. + size_t remaining() const { return remaining_; } + + // Resets this instance to an empty value. + void Reset() { navigator_.Reset(); } + + // Initializes this instance with `tree`. `tree` must not be null. + // Returns a reference to the first data edge of the provided tree. + absl::string_view Init(CordRepBtree* tree); + + // Navigates to and returns the next data edge of the referenced tree. + // Returns an empty string_view if an attempt is made to read beyond the end + // of the tree, i.e.: if `remaining()` is zero indicating an EOF condition. + // Requires that the current instance is not empty. + absl::string_view Next(); + + // Skips the provided amount of bytes and returns a reference to the data + // directly following the skipped bytes. + absl::string_view Skip(size_t skip); + + // Reads `n` bytes into `tree`. + // If `chunk_size` is zero, starts reading at the next data edge. If + // `chunk_size` is non zero, the read starts at the last `chunk_size` bytes of + // the last returned data edge. Effectively, this means that the read starts + // at offset `consumed() - chunk_size`. + // Requires that `chunk_size` is less than or equal to the length of the + // last returned data edge. The purpose of `chunk_size` is to simplify code + // partially consuming a returned chunk and wanting to include the remaining + // bytes in the Read call. For example, the below code will read 1000 bytes of + // data into a cord tree if the first chunk starts with "big:": + // + // CordRepBtreeReader reader; + // absl::string_view sv = reader.Init(tree); + // if (absl::StartsWith(sv, "big:")) { + // CordRepBtree tree; + // sv = reader.Read(1000, sv.size() - 4 /* "big:" */, &tree); + // } + // + // This method will return an empty string view if all remaining data was + // read. If `n` exceeded the amount of remaining data this function will + // return an empty string view and `tree` will be set to nullptr. + // In both cases, `consumed` will be set to `length`. + absl::string_view Read(size_t n, size_t chunk_size, CordRep*& tree); + + // Navigates to the chunk at offset `offset`. + // Returns a reference into the navigated to chunk, adjusted for the relative + // position of `offset` into that chunk. For example, calling `Seek(13)` on a + // cord tree containing 2 chunks of 10 and 20 bytes respectively will return + // a string view into the second chunk starting at offset 3 with a size of 17. + // Returns an empty string view if `offset` is equal to or greater than the + // length of the referenced tree. + absl::string_view Seek(size_t offset); + + private: + size_t remaining_ = 0; + CordRepBtreeNavigator navigator_; +}; + +inline size_t CordRepBtreeReader::length() const { + assert(btree() != nullptr); + return btree()->length; +} + +inline absl::string_view CordRepBtreeReader::Init(CordRepBtree* tree) { + assert(tree != nullptr); + const CordRep* edge = navigator_.InitFirst(tree); + remaining_ = tree->length - edge->length; + return EdgeData(edge); +} + +inline absl::string_view CordRepBtreeReader::Next() { + if (remaining_ == 0) return {}; + const CordRep* edge = navigator_.Next(); + assert(edge != nullptr); + remaining_ -= edge->length; + return EdgeData(edge); +} + +inline absl::string_view CordRepBtreeReader::Skip(size_t skip) { + // As we are always positioned on the last 'consumed' edge, we + // need to skip the current edge as well as `skip`. + const size_t edge_length = navigator_.Current()->length; + CordRepBtreeNavigator::Position pos = navigator_.Skip(skip + edge_length); + if (ABSL_PREDICT_FALSE(pos.edge == nullptr)) { + remaining_ = 0; + return {}; + } + // The combined length of all edges skipped before `pos.edge` is `skip - + // pos.offset`, all of which are 'consumed', as well as the current edge. + remaining_ -= skip - pos.offset + pos.edge->length; + return EdgeData(pos.edge).substr(pos.offset); +} + +inline absl::string_view CordRepBtreeReader::Seek(size_t offset) { + const CordRepBtreeNavigator::Position pos = navigator_.Seek(offset); + if (ABSL_PREDICT_FALSE(pos.edge == nullptr)) { + remaining_ = 0; + return {}; + } + absl::string_view chunk = EdgeData(pos.edge).substr(pos.offset); + remaining_ = length() - offset - chunk.length(); + return chunk; +} + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_CORD_REP_BTREE_READER_H_ diff --git a/Siv3D/include/ThirdParty/absl/strings/internal/cord_rep_consume.h b/Siv3D/include/ThirdParty/absl/strings/internal/cord_rep_consume.h new file mode 100644 index 0000000000..bece1874ac --- /dev/null +++ b/Siv3D/include/ThirdParty/absl/strings/internal/cord_rep_consume.h @@ -0,0 +1,47 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_INTERNAL_CORD_REP_CONSUME_H_ +#define ABSL_STRINGS_INTERNAL_CORD_REP_CONSUME_H_ + +#include + +#include "absl/functional/function_ref.h" +#include "absl/strings/internal/cord_internal.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// Consume() and ReverseConsume() consume CONCAT based trees and invoke the +// provided functor with the contained nodes in the proper forward or reverse +// order, which is used to convert CONCAT trees into other tree or cord data. +// All CONCAT and SUBSTRING nodes are processed internally. The 'offset` +// parameter of the functor is non-zero for any nodes below SUBSTRING nodes. +// It's up to the caller to form these back into SUBSTRING nodes or otherwise +// store offset / prefix information. These functions are intended to be used +// only for migration / transitional code where due to factors such as ODR +// violations, we can not 100% guarantee that all code respects 'new format' +// settings and flags, so we need to be able to parse old data on the fly until +// all old code is deprecated / no longer the default format. +void Consume(CordRep* rep, + FunctionRef consume_fn); +void ReverseConsume(CordRep* rep, + FunctionRef consume_fn); + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_CORD_REP_CONSUME_H_ diff --git a/Siv3D/include/ThirdParty/absl/strings/internal/cord_rep_crc.h b/Siv3D/include/ThirdParty/absl/strings/internal/cord_rep_crc.h new file mode 100644 index 0000000000..379d7a604e --- /dev/null +++ b/Siv3D/include/ThirdParty/absl/strings/internal/cord_rep_crc.h @@ -0,0 +1,103 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_INTERNAL_CORD_REP_CRC_H_ +#define ABSL_STRINGS_INTERNAL_CORD_REP_CRC_H_ + +#include +#include + +#include "absl/base/config.h" +#include "absl/base/optimization.h" +#include "absl/crc/internal/crc_cord_state.h" +#include "absl/strings/internal/cord_internal.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// CordRepCrc is a CordRep node intended only to appear at the top level of a +// cord tree. It associates an "expected CRC" with the contained data, to allow +// for easy passage of checksum data in Cord data flows. +// +// From Cord's perspective, the crc value has no semantics; any validation of +// the contained checksum is the user's responsibility. +struct CordRepCrc : public CordRep { + CordRep* child; + absl::crc_internal::CrcCordState crc_cord_state; + + // Consumes `child` and returns a CordRepCrc prefixed tree containing `child`. + // If the specified `child` is itself a CordRepCrc node, then this method + // either replaces the existing node, or directly updates the crc state in it + // depending on the node being shared or not, i.e.: refcount.IsOne(). + // `child` must only be null if the Cord is empty. Never returns null. + static CordRepCrc* New(CordRep* child, crc_internal::CrcCordState state); + + // Destroys (deletes) the provided node. `node` must not be null. + static void Destroy(CordRepCrc* node); +}; + +// Consumes `rep` and returns a CordRep* with any outer CordRepCrc wrapper +// removed. This is usually a no-op (returning `rep`), but this will remove and +// unref an outer CordRepCrc node. +inline CordRep* RemoveCrcNode(CordRep* rep) { + assert(rep != nullptr); + if (ABSL_PREDICT_FALSE(rep->IsCrc())) { + CordRep* child = rep->crc()->child; + if (rep->refcount.IsOne()) { + delete rep->crc(); + } else { + CordRep::Ref(child); + CordRep::Unref(rep); + } + return child; + } + return rep; +} + +// Returns `rep` if it is not a CordRepCrc node, or its child if it is. +// Does not consume or create a reference on `rep` or the returned value. +inline CordRep* SkipCrcNode(CordRep* rep) { + assert(rep != nullptr); + if (ABSL_PREDICT_FALSE(rep->IsCrc())) { + return rep->crc()->child; + } else { + return rep; + } +} + +inline const CordRep* SkipCrcNode(const CordRep* rep) { + assert(rep != nullptr); + if (ABSL_PREDICT_FALSE(rep->IsCrc())) { + return rep->crc()->child; + } else { + return rep; + } +} + +inline CordRepCrc* CordRep::crc() { + assert(IsCrc()); + return static_cast(this); +} + +inline const CordRepCrc* CordRep::crc() const { + assert(IsCrc()); + return static_cast(this); +} + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_CORD_REP_CRC_H_ diff --git a/Siv3D/include/ThirdParty/absl/strings/internal/cord_rep_flat.h b/Siv3D/include/ThirdParty/absl/strings/internal/cord_rep_flat.h new file mode 100644 index 0000000000..27c4b21eb5 --- /dev/null +++ b/Siv3D/include/ThirdParty/absl/strings/internal/cord_rep_flat.h @@ -0,0 +1,195 @@ +// Copyright 2020 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_INTERNAL_CORD_REP_FLAT_H_ +#define ABSL_STRINGS_INTERNAL_CORD_REP_FLAT_H_ + +#include +#include +#include +#include + +#include "absl/base/config.h" +#include "absl/base/macros.h" +#include "absl/strings/internal/cord_internal.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// Note: all constants below are never ODR used and internal to cord, we define +// these as static constexpr to avoid 'in struct' definition and usage clutter. + +// Largest and smallest flat node lengths we are willing to allocate +// Flat allocation size is stored in tag, which currently can encode sizes up +// to 4K, encoded as multiple of either 8 or 32 bytes. +// If we allow for larger sizes, we need to change this to 8/64, 16/128, etc. +// kMinFlatSize is bounded by tag needing to be at least FLAT * 8 bytes, and +// ideally a 'nice' size aligning with allocation and cacheline sizes like 32. +// kMaxFlatSize is bounded by the size resulting in a computed tag no greater +// than MAX_FLAT_TAG. MAX_FLAT_TAG provides for additional 'high' tag values. +static constexpr size_t kFlatOverhead = offsetof(CordRep, storage); +static constexpr size_t kMinFlatSize = 32; +static constexpr size_t kMaxFlatSize = 4096; +static constexpr size_t kMaxFlatLength = kMaxFlatSize - kFlatOverhead; +static constexpr size_t kMinFlatLength = kMinFlatSize - kFlatOverhead; +static constexpr size_t kMaxLargeFlatSize = 256 * 1024; +static constexpr size_t kMaxLargeFlatLength = kMaxLargeFlatSize - kFlatOverhead; + +// kTagBase should make the Size <--> Tag computation resilient +// against changes to the value of FLAT when we add a new tag.. +static constexpr uint8_t kTagBase = FLAT - 4; + +// Converts the provided rounded size to the corresponding tag +constexpr uint8_t AllocatedSizeToTagUnchecked(size_t size) { + return static_cast(size <= 512 ? kTagBase + size / 8 + : size <= 8192 + ? kTagBase + 512 / 8 + size / 64 - 512 / 64 + : kTagBase + 512 / 8 + ((8192 - 512) / 64) + + size / 4096 - 8192 / 4096); +} + +// Converts the provided tag to the corresponding allocated size +constexpr size_t TagToAllocatedSize(uint8_t tag) { + return (tag <= kTagBase + 512 / 8) ? tag * 8 - kTagBase * 8 + : (tag <= kTagBase + (512 / 8) + ((8192 - 512) / 64)) + ? 512 + tag * 64 - kTagBase * 64 - 512 / 8 * 64 + : 8192 + tag * 4096 - kTagBase * 4096 - + ((512 / 8) + ((8192 - 512) / 64)) * 4096; +} + +static_assert(AllocatedSizeToTagUnchecked(kMinFlatSize) == FLAT, ""); +static_assert(AllocatedSizeToTagUnchecked(kMaxLargeFlatSize) == MAX_FLAT_TAG, + ""); + +// RoundUp logically performs `((n + m - 1) / m) * m` to round up to the nearest +// multiple of `m`, optimized for the invariant that `m` is a power of 2. +constexpr size_t RoundUp(size_t n, size_t m) { + return (n + m - 1) & (0 - m); +} + +// Returns the size to the nearest equal or larger value that can be +// expressed exactly as a tag value. +inline size_t RoundUpForTag(size_t size) { + return RoundUp(size, (size <= 512) ? 8 : (size <= 8192 ? 64 : 4096)); +} + +// Converts the allocated size to a tag, rounding down if the size +// does not exactly match a 'tag expressible' size value. The result is +// undefined if the size exceeds the maximum size that can be encoded in +// a tag, i.e., if size is larger than TagToAllocatedSize(). +inline uint8_t AllocatedSizeToTag(size_t size) { + const uint8_t tag = AllocatedSizeToTagUnchecked(size); + assert(tag <= MAX_FLAT_TAG); + return tag; +} + +// Converts the provided tag to the corresponding available data length +constexpr size_t TagToLength(uint8_t tag) { + return TagToAllocatedSize(tag) - kFlatOverhead; +} + +// Enforce that kMaxFlatSize maps to a well-known exact tag value. +static_assert(TagToAllocatedSize(MAX_FLAT_TAG) == kMaxLargeFlatSize, + "Bad tag logic"); + +struct CordRepFlat : public CordRep { + // Tag for explicit 'large flat' allocation + struct Large {}; + + // Creates a new flat node. + template + static CordRepFlat* NewImpl(size_t len, Args... args ABSL_ATTRIBUTE_UNUSED) { + if (len <= kMinFlatLength) { + len = kMinFlatLength; + } else if (len > max_flat_size - kFlatOverhead) { + len = max_flat_size - kFlatOverhead; + } + + // Round size up so it matches a size we can exactly express in a tag. + const size_t size = RoundUpForTag(len + kFlatOverhead); + void* const raw_rep = ::operator new(size); + // GCC 13 has a false-positive -Wstringop-overflow warning here. + #if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(13, 0) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wstringop-overflow" + #endif + CordRepFlat* rep = new (raw_rep) CordRepFlat(); + rep->tag = AllocatedSizeToTag(size); + #if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(13, 0) + #pragma GCC diagnostic pop + #endif + return rep; + } + + static CordRepFlat* New(size_t len) { return NewImpl(len); } + + static CordRepFlat* New(Large, size_t len) { + return NewImpl(len); + } + + // Deletes a CordRepFlat instance created previously through a call to New(). + // Flat CordReps are allocated and constructed with raw ::operator new and + // placement new, and must be destructed and deallocated accordingly. + static void Delete(CordRep*rep) { + assert(rep->tag >= FLAT && rep->tag <= MAX_FLAT_TAG); + +#if defined(__cpp_sized_deallocation) + size_t size = TagToAllocatedSize(rep->tag); + rep->~CordRep(); + ::operator delete(rep, size); +#else + rep->~CordRep(); + ::operator delete(rep); +#endif + } + + // Create a CordRepFlat containing `data`, with an optional additional + // extra capacity of up to `extra` bytes. Requires that `data.size()` + // is less than kMaxFlatLength. + static CordRepFlat* Create(absl::string_view data, size_t extra = 0) { + assert(data.size() <= kMaxFlatLength); + CordRepFlat* flat = New(data.size() + (std::min)(extra, kMaxFlatLength)); + memcpy(flat->Data(), data.data(), data.size()); + flat->length = data.size(); + return flat; + } + + // Returns a pointer to the data inside this flat rep. + char* Data() { return reinterpret_cast(storage); } + const char* Data() const { return reinterpret_cast(storage); } + + // Returns the maximum capacity (payload size) of this instance. + size_t Capacity() const { return TagToLength(tag); } + + // Returns the allocated size (payload + overhead) of this instance. + size_t AllocatedSize() const { return TagToAllocatedSize(tag); } +}; + +// Now that CordRepFlat is defined, we can define CordRep's helper casts: +inline CordRepFlat* CordRep::flat() { + assert(tag >= FLAT && tag <= MAX_FLAT_TAG); + return reinterpret_cast(this); +} + +inline const CordRepFlat* CordRep::flat() const { + assert(tag >= FLAT && tag <= MAX_FLAT_TAG); + return reinterpret_cast(this); +} + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_CORD_REP_FLAT_H_ diff --git a/Siv3D/include/ThirdParty/absl/strings/internal/cord_rep_ring.h b/Siv3D/include/ThirdParty/absl/strings/internal/cord_rep_ring.h new file mode 100644 index 0000000000..79a2fdb1c3 --- /dev/null +++ b/Siv3D/include/ThirdParty/absl/strings/internal/cord_rep_ring.h @@ -0,0 +1,607 @@ +// Copyright 2020 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_INTERNAL_CORD_REP_RING_H_ +#define ABSL_STRINGS_INTERNAL_CORD_REP_RING_H_ + +#include +#include +#include +#include +#include +#include + +#include "absl/container/internal/layout.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_flat.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// All operations modifying a ring buffer are implemented as static methods +// requiring a CordRepRing instance with a reference adopted by the method. +// +// The methods return the modified ring buffer, which may be equal to the input +// if the input was not shared, and having large enough capacity to accommodate +// any newly added node(s). Otherwise, a copy of the input rep with the new +// node(s) added is returned. +// +// Any modification on non shared ring buffers with enough capacity will then +// require minimum atomic operations. Caller should where possible provide +// reasonable `extra` hints for both anticipated extra `flat` byte space, as +// well as anticipated extra nodes required for complex operations. +// +// Example of code creating a ring buffer, adding some data to it, +// and discarding the buffer when done: +// +// void FunWithRings() { +// // Create ring with 3 flats +// CordRep* flat = CreateFlat("Hello"); +// CordRepRing* ring = CordRepRing::Create(flat, 2); +// ring = CordRepRing::Append(ring, CreateFlat(" ")); +// ring = CordRepRing::Append(ring, CreateFlat("world")); +// DoSomethingWithRing(ring); +// CordRep::Unref(ring); +// } +// +// Example of code Copying an existing ring buffer and modifying it: +// +// void MoreFunWithRings(CordRepRing* src) { +// CordRepRing* ring = CordRep::Ref(src)->ring(); +// ring = CordRepRing::Append(ring, CreateFlat("Hello")); +// ring = CordRepRing::Append(ring, CreateFlat(" ")); +// ring = CordRepRing::Append(ring, CreateFlat("world")); +// DoSomethingWithRing(ring); +// CordRep::Unref(ring); +// } +// +class CordRepRing : public CordRep { + public: + // `pos_type` represents a 'logical position'. A CordRepRing instance has a + // `begin_pos` (default 0), and each node inside the buffer will have an + // `end_pos` which is the `end_pos` of the previous node (or `begin_pos`) plus + // this node's length. The purpose is to allow for a binary search on this + // position, while allowing O(1) prepend and append operations. + using pos_type = size_t; + + // `index_type` is the type for the `head`, `tail` and `capacity` indexes. + // Ring buffers are limited to having no more than four billion entries. + using index_type = uint32_t; + + // `offset_type` is the type for the data offset inside a child rep's data. + using offset_type = uint32_t; + + // Position holds the node index and relative offset into the node for + // some physical offset in the contained data as returned by the Find() + // and FindTail() methods. + struct Position { + index_type index; + size_t offset; + }; + + // The maximum # of child nodes that can be hosted inside a CordRepRing. + static constexpr size_t kMaxCapacity = (std::numeric_limits::max)(); + + // CordRepring can not be default constructed, moved, copied or assigned. + CordRepRing() = delete; + CordRepRing(const CordRepRing&) = delete; + CordRepRing& operator=(const CordRepRing&) = delete; + + // Returns true if this instance is valid, false if some or all of the + // invariants are broken. Intended for debug purposes only. + // `output` receives an explanation of the broken invariants. + bool IsValid(std::ostream& output) const; + + // Returns the size in bytes for a CordRepRing with `capacity' entries. + static constexpr size_t AllocSize(size_t capacity); + + // Returns the distance in bytes from `pos` to `end_pos`. + static constexpr size_t Distance(pos_type pos, pos_type end_pos); + + // Creates a new ring buffer from the provided `rep`. Adopts a reference + // on `rep`. The returned ring buffer has a capacity of at least `extra + 1` + static CordRepRing* Create(CordRep* child, size_t extra = 0); + + // `head`, `tail` and `capacity` indexes defining the ring buffer boundaries. + index_type head() const { return head_; } + index_type tail() const { return tail_; } + index_type capacity() const { return capacity_; } + + // Returns the number of entries in this instance. + index_type entries() const { return entries(head_, tail_); } + + // Returns the logical begin position of this instance. + pos_type begin_pos() const { return begin_pos_; } + + // Returns the number of entries for a given head-tail range. + // Requires `head` and `tail` values to be less than `capacity()`. + index_type entries(index_type head, index_type tail) const { + assert(head < capacity_ && tail < capacity_); + return tail - head + ((tail > head) ? 0 : capacity_); + } + + // Returns the logical end position of entry `index`. + pos_type const& entry_end_pos(index_type index) const { + assert(IsValidIndex(index)); + return Layout::Partial().Pointer<0>(data_)[index]; + } + + // Returns the child pointer of entry `index`. + CordRep* const& entry_child(index_type index) const { + assert(IsValidIndex(index)); + return Layout::Partial(capacity()).Pointer<1>(data_)[index]; + } + + // Returns the data offset of entry `index` + offset_type const& entry_data_offset(index_type index) const { + assert(IsValidIndex(index)); + return Layout::Partial(capacity(), capacity()).Pointer<2>(data_)[index]; + } + + // Appends the provided child node to the `rep` instance. + // Adopts a reference from `rep` and `child` which may not be null. + // If the provided child is a FLAT or EXTERNAL node, or a SUBSTRING node + // containing a FLAT or EXTERNAL node, then flat or external the node is added + // 'as is', with an offset added for the SUBSTRING case. + // If the provided child is a RING or CONCAT tree, or a SUBSTRING of a RING or + // CONCAT tree, then all child nodes not excluded by any start offset or + // length values are added recursively. + static CordRepRing* Append(CordRepRing* rep, CordRep* child); + + // Appends the provided string data to the `rep` instance. + // This function will attempt to utilize any remaining capacity in the last + // node of the input if that node is not shared (directly or indirectly), and + // of type FLAT. Remaining data will be added as one or more FLAT nodes. + // Any last node added to the ring buffer will be allocated with up to + // `extra` bytes of capacity for (anticipated) subsequent append actions. + static CordRepRing* Append(CordRepRing* rep, string_view data, + size_t extra = 0); + + // Prepends the provided child node to the `rep` instance. + // Adopts a reference from `rep` and `child` which may not be null. + // If the provided child is a FLAT or EXTERNAL node, or a SUBSTRING node + // containing a FLAT or EXTERNAL node, then flat or external the node is + // prepended 'as is', with an optional offset added for the SUBSTRING case. + // If the provided child is a RING or CONCAT tree, or a SUBSTRING of a RING + // or CONCAT tree, then all child nodes not excluded by any start offset or + // length values are added recursively. + static CordRepRing* Prepend(CordRepRing* rep, CordRep* child); + + // Prepends the provided string data to the `rep` instance. + // This function will attempt to utilize any remaining capacity in the first + // node of the input if that node is not shared (directly or indirectly), and + // of type FLAT. Remaining data will be added as one or more FLAT nodes. + // Any first node prepnded to the ring buffer will be allocated with up to + // `extra` bytes of capacity for (anticipated) subsequent prepend actions. + static CordRepRing* Prepend(CordRepRing* rep, string_view data, + size_t extra = 0); + + // Returns a span referencing potentially unused capacity in the last node. + // The returned span may be empty if no such capacity is available, or if the + // current instance is shared. Else, a span of size `n <= size` is returned. + // If non empty, the ring buffer is adjusted to the new length, with the newly + // added capacity left uninitialized. Callers should assign a value to the + // entire span before any other operations on this instance. + Span GetAppendBuffer(size_t size); + + // Returns a span referencing potentially unused capacity in the first node. + // This function is identical to GetAppendBuffer except that it returns a span + // referencing up to `size` capacity directly before the existing data. + Span GetPrependBuffer(size_t size); + + // Returns a cord ring buffer containing `len` bytes of data starting at + // `offset`. If the input is not shared, this function will remove all head + // and tail child nodes outside of the requested range, and adjust the new + // head and tail nodes as required. If the input is shared, this function + // returns a new instance sharing some or all of the nodes from the input. + static CordRepRing* SubRing(CordRepRing* r, size_t offset, size_t len, + size_t extra = 0); + + // Returns a cord ring buffer with the first `len` bytes removed. + // If the input is not shared, this function will remove all head child nodes + // fully inside the first `length` bytes, and adjust the new head as required. + // If the input is shared, this function returns a new instance sharing some + // or all of the nodes from the input. + static CordRepRing* RemoveSuffix(CordRepRing* r, size_t len, + size_t extra = 0); + + // Returns a cord ring buffer with the last `len` bytes removed. + // If the input is not shared, this function will remove all head child nodes + // fully inside the first `length` bytes, and adjust the new head as required. + // If the input is shared, this function returns a new instance sharing some + // or all of the nodes from the input. + static CordRepRing* RemovePrefix(CordRepRing* r, size_t len, + size_t extra = 0); + + // Returns the character at `offset`. Requires that `offset < length`. + char GetCharacter(size_t offset) const; + + // Returns true if this instance manages a single contiguous buffer, in which + // case the (optional) output parameter `fragment` is set. Otherwise, the + // function returns false, and `fragment` is left unchanged. + bool IsFlat(absl::string_view* fragment) const; + + // Returns true if the data starting at `offset` with length `len` is + // managed by this instance inside a single contiguous buffer, in which case + // the (optional) output parameter `fragment` is set to the contiguous memory + // starting at offset `offset` with length `length`. Otherwise, the function + // returns false, and `fragment` is left unchanged. + bool IsFlat(size_t offset, size_t len, absl::string_view* fragment) const; + + // Testing only: set capacity to requested capacity. + void SetCapacityForTesting(size_t capacity); + + // Returns the CordRep data pointer for the provided CordRep. + // Requires that the provided `rep` is either a FLAT or EXTERNAL CordRep. + static const char* GetLeafData(const CordRep* rep); + + // Returns the CordRep data pointer for the provided CordRep. + // Requires that `rep` is either a FLAT, EXTERNAL, or SUBSTRING CordRep. + static const char* GetRepData(const CordRep* rep); + + // Advances the provided position, wrapping around capacity as needed. + // Requires `index` < capacity() + inline index_type advance(index_type index) const; + + // Advances the provided position by 'n`, wrapping around capacity as needed. + // Requires `index` < capacity() and `n` <= capacity. + inline index_type advance(index_type index, index_type n) const; + + // Retreats the provided position, wrapping around 0 as needed. + // Requires `index` < capacity() + inline index_type retreat(index_type index) const; + + // Retreats the provided position by 'n', wrapping around 0 as needed. + // Requires `index` < capacity() + inline index_type retreat(index_type index, index_type n) const; + + // Returns the logical begin position of entry `index` + pos_type const& entry_begin_pos(index_type index) const { + return (index == head_) ? begin_pos_ : entry_end_pos(retreat(index)); + } + + // Returns the physical start offset of entry `index` + size_t entry_start_offset(index_type index) const { + return Distance(begin_pos_, entry_begin_pos(index)); + } + + // Returns the physical end offset of entry `index` + size_t entry_end_offset(index_type index) const { + return Distance(begin_pos_, entry_end_pos(index)); + } + + // Returns the data length for entry `index` + size_t entry_length(index_type index) const { + return Distance(entry_begin_pos(index), entry_end_pos(index)); + } + + // Returns the data for entry `index` + absl::string_view entry_data(index_type index) const; + + // Returns the position for `offset` as {index, prefix}. `index` holds the + // index of the entry at the specified offset and `prefix` holds the relative + // offset inside that entry. + // Requires `offset` < length. + // + // For example we can implement GetCharacter(offset) as: + // char GetCharacter(size_t offset) { + // Position pos = this->Find(offset); + // return this->entry_data(pos.pos)[pos.offset]; + // } + inline Position Find(size_t offset) const; + + // Find starting at `head` + inline Position Find(index_type head, size_t offset) const; + + // Returns the tail position for `offset` as {tail index, suffix}. + // `tail index` holds holds the index of the entry holding the offset directly + // before 'offset` advanced by one. 'suffix` holds the relative offset from + // that relative offset in the entry to the end of the entry. + // For example, FindTail(length) will return {tail(), 0}, FindTail(length - 5) + // will return {retreat(tail), 5)} provided the preceding entry contains at + // least 5 bytes of data. + // Requires offset >= 1 && offset <= length. + // + // This function is very useful in functions that need to clip the end of some + // ring buffer such as 'RemovePrefix'. + // For example, we could implement RemovePrefix for non shared instances as: + // void RemoveSuffix(size_t n) { + // Position pos = FindTail(length - n); + // UnrefEntries(pos.pos, this->tail_); + // this->tail_ = pos.pos; + // entry(retreat(pos.pos)).end_pos -= pos.offset; + // } + inline Position FindTail(size_t offset) const; + + // Find tail starting at `head` + inline Position FindTail(index_type head, size_t offset) const; + + // Invokes f(index_type index) for each entry inside the range [head, tail> + template + void ForEach(index_type head, index_type tail, F&& f) const { + index_type n1 = (tail > head) ? tail : capacity_; + for (index_type i = head; i < n1; ++i) f(i); + if (tail <= head) { + for (index_type i = 0; i < tail; ++i) f(i); + } + } + + // Invokes f(index_type index) for each entry inside this instance. + template + void ForEach(F&& f) const { + ForEach(head_, tail_, std::forward(f)); + } + + // Dump this instance's data tp stream `s` in human readable format, excluding + // the actual data content itself. Intended for debug purposes only. + friend std::ostream& operator<<(std::ostream& s, const CordRepRing& rep); + + private: + enum class AddMode { kAppend, kPrepend }; + + using Layout = container_internal::Layout; + + class Filler; + class Transaction; + class CreateTransaction; + + static constexpr size_t kLayoutAlignment = Layout::Partial().Alignment(); + + // Creates a new CordRepRing. + explicit CordRepRing(index_type capacity) : capacity_(capacity) {} + + // Returns true if `index` is a valid index into this instance. + bool IsValidIndex(index_type index) const; + + // Debug use only: validates the provided CordRepRing invariants. + // Verification of all CordRepRing methods can be enabled by defining + // EXTRA_CORD_RING_VALIDATION, i.e.: `--copts=-DEXTRA_CORD_RING_VALIDATION` + // Verification is VERY expensive, so only do it for debugging purposes. + static CordRepRing* Validate(CordRepRing* rep, const char* file = nullptr, + int line = 0); + + // Allocates a CordRepRing large enough to hold `capacity + extra' entries. + // The returned capacity may be larger if the allocated memory allows for it. + // The maximum capacity of a CordRepRing is capped at kMaxCapacity. + // Throws `std::length_error` if `capacity + extra' exceeds kMaxCapacity. + static CordRepRing* New(size_t capacity, size_t extra); + + // Deallocates (but does not destroy) the provided ring buffer. + static void Delete(CordRepRing* rep); + + // Destroys the provided ring buffer, decrementing the reference count of all + // contained child CordReps. The provided 1\`rep` should have a ref count of + // one (pre decrement destroy call observing `refcount.IsOne()`) or zero + // (post decrement destroy call observing `!refcount.Decrement()`). + static void Destroy(CordRepRing* rep); + + // Returns a mutable reference to the logical end position array. + pos_type* entry_end_pos() { + return Layout::Partial().Pointer<0>(data_); + } + + // Returns a mutable reference to the child pointer array. + CordRep** entry_child() { + return Layout::Partial(capacity()).Pointer<1>(data_); + } + + // Returns a mutable reference to the data offset array. + offset_type* entry_data_offset() { + return Layout::Partial(capacity(), capacity()).Pointer<2>(data_); + } + + // Find implementations for the non fast path 0 / length cases. + Position FindSlow(index_type head, size_t offset) const; + Position FindTailSlow(index_type head, size_t offset) const; + + // Finds the index of the first node that is inside a reasonable distance + // of the node at `offset` from which we can continue with a linear search. + template + index_type FindBinary(index_type head, index_type tail, size_t offset) const; + + // Fills the current (initialized) instance from the provided source, copying + // entries [head, tail). Adds a reference to copied entries if `ref` is true. + template + void Fill(const CordRepRing* src, index_type head, index_type tail); + + // Create a copy of 'rep', copying all entries [head, tail), allocating room + // for `extra` entries. Adds a reference on all copied entries. + static CordRepRing* Copy(CordRepRing* rep, index_type head, index_type tail, + size_t extra = 0); + + // Returns a Mutable CordRepRing reference from `rep` with room for at least + // `extra` additional nodes. Adopts a reference count from `rep`. + // This function will return `rep` if, and only if: + // - rep.entries + extra <= rep.capacity + // - rep.refcount == 1 + // Otherwise, this function will create a new copy of `rep` with additional + // capacity to satisfy `extra` extra nodes, and unref the old `rep` instance. + // + // If a new CordRepRing can not be allocated, or the new capacity would exceed + // the maximum capacity, then the input is consumed only, and an exception is + // thrown. + static CordRepRing* Mutable(CordRepRing* rep, size_t extra); + + // Slow path for Append(CordRepRing* rep, CordRep* child). This function is + // exercised if the provided `child` in Append() is not a leaf node, i.e., a + // ring buffer or old (concat) cord tree. + static CordRepRing* AppendSlow(CordRepRing* rep, CordRep* child); + + // Appends the provided leaf node. Requires `child` to be FLAT or EXTERNAL. + static CordRepRing* AppendLeaf(CordRepRing* rep, CordRep* child, + size_t offset, size_t length); + + // Prepends the provided leaf node. Requires `child` to be FLAT or EXTERNAL. + static CordRepRing* PrependLeaf(CordRepRing* rep, CordRep* child, + size_t offset, size_t length); + + // Slow path for Prepend(CordRepRing* rep, CordRep* child). This function is + // exercised if the provided `child` in Prepend() is not a leaf node, i.e., a + // ring buffer or old (concat) cord tree. + static CordRepRing* PrependSlow(CordRepRing* rep, CordRep* child); + + // Slow path for Create(CordRep* child, size_t extra). This function is + // exercised if the provided `child` in Prepend() is not a leaf node, i.e., a + // ring buffer or old (concat) cord tree. + static CordRepRing* CreateSlow(CordRep* child, size_t extra); + + // Creates a new ring buffer from the provided `child` leaf node. Requires + // `child` to be FLAT or EXTERNAL. on `rep`. + // The returned ring buffer has a capacity of at least `1 + extra` + static CordRepRing* CreateFromLeaf(CordRep* child, size_t offset, + size_t length, size_t extra); + + // Appends or prepends (depending on AddMode) the ring buffer in `ring' to + // `rep` starting at `offset` with length `len`. + template + static CordRepRing* AddRing(CordRepRing* rep, CordRepRing* ring, + size_t offset, size_t len); + + // Increases the data offset for entry `index` by `n`. + void AddDataOffset(index_type index, size_t n); + + // Decreases the length for entry `index` by `n`. + void SubLength(index_type index, size_t n); + + index_type head_; + index_type tail_; + index_type capacity_; + pos_type begin_pos_; + + alignas(kLayoutAlignment) char data_[kLayoutAlignment]; + + friend struct CordRep; +}; + +constexpr size_t CordRepRing::AllocSize(size_t capacity) { + return sizeof(CordRepRing) - sizeof(data_) + + Layout(capacity, capacity, capacity).AllocSize(); +} + +inline constexpr size_t CordRepRing::Distance(pos_type pos, pos_type end_pos) { + return (end_pos - pos); +} + +inline const char* CordRepRing::GetLeafData(const CordRep* rep) { + return rep->tag != EXTERNAL ? rep->flat()->Data() : rep->external()->base; +} + +inline const char* CordRepRing::GetRepData(const CordRep* rep) { + if (rep->tag >= FLAT) return rep->flat()->Data(); + if (rep->tag == EXTERNAL) return rep->external()->base; + return GetLeafData(rep->substring()->child) + rep->substring()->start; +} + +inline CordRepRing::index_type CordRepRing::advance(index_type index) const { + assert(index < capacity_); + return ++index == capacity_ ? 0 : index; +} + +inline CordRepRing::index_type CordRepRing::advance(index_type index, + index_type n) const { + assert(index < capacity_ && n <= capacity_); + return (index += n) >= capacity_ ? index - capacity_ : index; +} + +inline CordRepRing::index_type CordRepRing::retreat(index_type index) const { + assert(index < capacity_); + return (index > 0 ? index : capacity_) - 1; +} + +inline CordRepRing::index_type CordRepRing::retreat(index_type index, + index_type n) const { + assert(index < capacity_ && n <= capacity_); + return index >= n ? index - n : capacity_ - n + index; +} + +inline absl::string_view CordRepRing::entry_data(index_type index) const { + size_t data_offset = entry_data_offset(index); + return {GetRepData(entry_child(index)) + data_offset, entry_length(index)}; +} + +inline bool CordRepRing::IsValidIndex(index_type index) const { + if (index >= capacity_) return false; + return (tail_ > head_) ? (index >= head_ && index < tail_) + : (index >= head_ || index < tail_); +} + +#ifndef EXTRA_CORD_RING_VALIDATION +inline CordRepRing* CordRepRing::Validate(CordRepRing* rep, + const char* /*file*/, int /*line*/) { + return rep; +} +#endif + +inline CordRepRing::Position CordRepRing::Find(size_t offset) const { + assert(offset < length); + return (offset == 0) ? Position{head_, 0} : FindSlow(head_, offset); +} + +inline CordRepRing::Position CordRepRing::Find(index_type head, + size_t offset) const { + assert(offset < length); + assert(IsValidIndex(head) && offset >= entry_start_offset(head)); + return (offset == 0) ? Position{head_, 0} : FindSlow(head, offset); +} + +inline CordRepRing::Position CordRepRing::FindTail(size_t offset) const { + assert(offset > 0 && offset <= length); + return (offset == length) ? Position{tail_, 0} : FindTailSlow(head_, offset); +} + +inline CordRepRing::Position CordRepRing::FindTail(index_type head, + size_t offset) const { + assert(offset > 0 && offset <= length); + assert(IsValidIndex(head) && offset >= entry_start_offset(head) + 1); + return (offset == length) ? Position{tail_, 0} : FindTailSlow(head, offset); +} + +// Now that CordRepRing is defined, we can define CordRep's helper casts: +inline CordRepRing* CordRep::ring() { + assert(IsRing()); + return static_cast(this); +} + +inline const CordRepRing* CordRep::ring() const { + assert(IsRing()); + return static_cast(this); +} + +inline bool CordRepRing::IsFlat(absl::string_view* fragment) const { + if (entries() == 1) { + if (fragment) *fragment = entry_data(head()); + return true; + } + return false; +} + +inline bool CordRepRing::IsFlat(size_t offset, size_t len, + absl::string_view* fragment) const { + const Position pos = Find(offset); + const absl::string_view data = entry_data(pos.index); + if (data.length() >= len && data.length() - len >= pos.offset) { + if (fragment) *fragment = data.substr(pos.offset, len); + return true; + } + return false; +} + +std::ostream& operator<<(std::ostream& s, const CordRepRing& rep); + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_CORD_REP_RING_H_ diff --git a/Siv3D/include/ThirdParty/absl/strings/internal/cord_rep_ring_reader.h b/Siv3D/include/ThirdParty/absl/strings/internal/cord_rep_ring_reader.h new file mode 100644 index 0000000000..7ceeaa000e --- /dev/null +++ b/Siv3D/include/ThirdParty/absl/strings/internal/cord_rep_ring_reader.h @@ -0,0 +1,118 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_INTERNAL_CORD_REP_RING_READER_H_ +#define ABSL_STRINGS_INTERNAL_CORD_REP_RING_READER_H_ + +#include +#include +#include + +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_ring.h" +#include "absl/strings/string_view.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// CordRepRingReader provides basic navigation over CordRepRing data. +class CordRepRingReader { + public: + // Returns true if this instance is not empty. + explicit operator bool() const { return ring_ != nullptr; } + + // Returns the ring buffer reference for this instance, or nullptr if empty. + CordRepRing* ring() const { return ring_; } + + // Returns the current node index inside the ring buffer for this instance. + // The returned value is undefined if this instance is empty. + CordRepRing::index_type index() const { return index_; } + + // Returns the current node inside the ring buffer for this instance. + // The returned value is undefined if this instance is empty. + CordRep* node() const { return ring_->entry_child(index_); } + + // Returns the length of the referenced ring buffer. + // Requires the current instance to be non empty. + size_t length() const { + assert(ring_); + return ring_->length; + } + + // Returns the end offset of the last navigated-to chunk, which represents the + // total bytes 'consumed' relative to the start of the ring. The returned + // value is never zero. For example, initializing a reader with a ring buffer + // with a first chunk of 19 bytes will return consumed() = 19. + // Requires the current instance to be non empty. + size_t consumed() const { + assert(ring_); + return ring_->entry_end_offset(index_); + } + + // Returns the number of bytes remaining beyond the last navigated-to chunk. + // Requires the current instance to be non empty. + size_t remaining() const { + assert(ring_); + return length() - consumed(); + } + + // Resets this instance to an empty value + void Reset() { ring_ = nullptr; } + + // Resets this instance to the start of `ring`. `ring` must not be null. + // Returns a reference into the first chunk of the provided ring. + absl::string_view Reset(CordRepRing* ring) { + assert(ring); + ring_ = ring; + index_ = ring_->head(); + return ring_->entry_data(index_); + } + + // Navigates to the next chunk inside the reference ring buffer. + // Returns a reference into the navigated-to chunk. + // Requires remaining() to be non zero. + absl::string_view Next() { + assert(remaining()); + index_ = ring_->advance(index_); + return ring_->entry_data(index_); + } + + // Navigates to the chunk at offset `offset`. + // Returns a reference into the navigated-to chunk, adjusted for the relative + // position of `offset` into that chunk. For example, calling Seek(13) on a + // ring buffer containing 2 chunks of 10 and 20 bytes respectively will return + // a string view into the second chunk starting at offset 3 with a size of 17. + // Requires `offset` to be less than `length()` + absl::string_view Seek(size_t offset) { + assert(offset < length()); + size_t current = ring_->entry_end_offset(index_); + CordRepRing::index_type hint = (offset >= current) ? index_ : ring_->head(); + const CordRepRing::Position head = ring_->Find(hint, offset); + index_ = head.index; + auto data = ring_->entry_data(head.index); + data.remove_prefix(head.offset); + return data; + } + + private: + CordRepRing* ring_ = nullptr; + CordRepRing::index_type index_; +}; + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_CORD_REP_RING_READER_H_ diff --git a/Siv3D/include/ThirdParty/absl/strings/internal/cord_rep_test_util.h b/Siv3D/include/ThirdParty/absl/strings/internal/cord_rep_test_util.h new file mode 100644 index 0000000000..18a0a19544 --- /dev/null +++ b/Siv3D/include/ThirdParty/absl/strings/internal/cord_rep_test_util.h @@ -0,0 +1,205 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_INTERNAL_CORD_REP_TEST_UTIL_H_ +#define ABSL_STRINGS_INTERNAL_CORD_REP_TEST_UTIL_H_ + +#include +#include +#include +#include +#include + +#include "absl/base/config.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_btree.h" +#include "absl/strings/internal/cord_rep_flat.h" +#include "absl/strings/string_view.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cordrep_testing { + +inline cord_internal::CordRepSubstring* MakeSubstring( + size_t start, size_t len, cord_internal::CordRep* rep) { + auto* sub = new cord_internal::CordRepSubstring; + sub->tag = cord_internal::SUBSTRING; + sub->start = start; + sub->length = len <= 0 ? rep->length - start + len : len; + sub->child = rep; + return sub; +} + +inline cord_internal::CordRepFlat* MakeFlat(absl::string_view value) { + assert(value.length() <= cord_internal::kMaxFlatLength); + auto* flat = cord_internal::CordRepFlat::New(value.length()); + flat->length = value.length(); + memcpy(flat->Data(), value.data(), value.length()); + return flat; +} + +// Creates an external node for testing +inline cord_internal::CordRepExternal* MakeExternal(absl::string_view s) { + struct Rep : public cord_internal::CordRepExternal { + std::string s; + explicit Rep(absl::string_view sv) : s(sv) { + this->tag = cord_internal::EXTERNAL; + this->base = s.data(); + this->length = s.length(); + this->releaser_invoker = [](cord_internal::CordRepExternal* self) { + delete static_cast(self); + }; + } + }; + return new Rep(s); +} + +inline std::string CreateRandomString(size_t n) { + absl::string_view data = + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789~!@#$%^&*()_+=-<>?:\"{}[]|"; + std::minstd_rand rnd; + std::uniform_int_distribution dist(0, data.size() - 1); + std::string s(n, ' '); + for (size_t i = 0; i < n; ++i) { + s[i] = data[dist(rnd)]; + } + return s; +} + +// Creates an array of flats from the provided string, chopping +// the provided string up into flats of size `chunk_size` characters +// resulting in roughly `data.size() / chunk_size` total flats. +inline std::vector CreateFlatsFromString( + absl::string_view data, size_t chunk_size) { + assert(chunk_size > 0); + std::vector flats; + for (absl::string_view s = data; !s.empty(); s.remove_prefix(chunk_size)) { + flats.push_back(MakeFlat(s.substr(0, chunk_size))); + } + return flats; +} + +inline cord_internal::CordRepBtree* CordRepBtreeFromFlats( + absl::Span flats) { + assert(!flats.empty()); + auto* node = cord_internal::CordRepBtree::Create(flats[0]); + for (size_t i = 1; i < flats.size(); ++i) { + node = cord_internal::CordRepBtree::Append(node, flats[i]); + } + return node; +} + +template +inline void CordVisitReps(cord_internal::CordRep* rep, Fn&& fn) { + fn(rep); + while (rep->tag == cord_internal::SUBSTRING) { + rep = rep->substring()->child; + fn(rep); + } + if (rep->tag == cord_internal::BTREE) { + for (cord_internal::CordRep* edge : rep->btree()->Edges()) { + CordVisitReps(edge, fn); + } + } +} + +template +inline std::vector CordCollectRepsIf( + Predicate&& predicate, cord_internal::CordRep* rep) { + std::vector reps; + CordVisitReps(rep, [&reps, &predicate](cord_internal::CordRep* rep) { + if (predicate(rep)) reps.push_back(rep); + }); + return reps; +} + +inline std::vector CordCollectReps( + cord_internal::CordRep* rep) { + std::vector reps; + auto fn = [&reps](cord_internal::CordRep* rep) { reps.push_back(rep); }; + CordVisitReps(rep, fn); + return reps; +} + +inline void CordToString(cord_internal::CordRep* rep, std::string& s) { + size_t offset = 0; + size_t length = rep->length; + while (rep->tag == cord_internal::SUBSTRING) { + offset += rep->substring()->start; + rep = rep->substring()->child; + } + if (rep->tag == cord_internal::BTREE) { + for (cord_internal::CordRep* edge : rep->btree()->Edges()) { + CordToString(edge, s); + } + } else if (rep->tag >= cord_internal::FLAT) { + s.append(rep->flat()->Data() + offset, length); + } else if (rep->tag == cord_internal::EXTERNAL) { + s.append(rep->external()->base + offset, length); + } else { + ABSL_RAW_LOG(FATAL, "Unsupported tag %d", rep->tag); + } +} + +inline std::string CordToString(cord_internal::CordRep* rep) { + std::string s; + s.reserve(rep->length); + CordToString(rep, s); + return s; +} + +// RAII Helper class to automatically unref reps on destruction. +class AutoUnref { + public: + ~AutoUnref() { + for (CordRep* rep : unrefs_) CordRep::Unref(rep); + } + + // Adds `rep` to the list of reps to be unreffed at destruction. + template + CordRepType* Add(CordRepType* rep) { + unrefs_.push_back(rep); + return rep; + } + + // Increments the reference count of `rep` by one, and adds it to + // the list of reps to be unreffed at destruction. + template + CordRepType* Ref(CordRepType* rep) { + unrefs_.push_back(CordRep::Ref(rep)); + return rep; + } + + // Increments the reference count of `rep` by one if `condition` is true, + // and adds it to the list of reps to be unreffed at destruction. + template + CordRepType* RefIf(bool condition, CordRepType* rep) { + if (condition) unrefs_.push_back(CordRep::Ref(rep)); + return rep; + } + + private: + using CordRep = absl::cord_internal::CordRep; + + std::vector unrefs_; +}; + +} // namespace cordrep_testing +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_CORD_REP_TEST_UTIL_H_ diff --git a/Siv3D/include/ThirdParty/absl/strings/internal/cordz_functions.h b/Siv3D/include/ThirdParty/absl/strings/internal/cordz_functions.h new file mode 100644 index 0000000000..ed108bf1b4 --- /dev/null +++ b/Siv3D/include/ThirdParty/absl/strings/internal/cordz_functions.h @@ -0,0 +1,77 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_INTERNAL_CORDZ_FUNCTIONS_H_ +#define ABSL_STRINGS_INTERNAL_CORDZ_FUNCTIONS_H_ + +#include + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/base/optimization.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// Returns the current sample rate. This represents the average interval +// between samples. +int32_t get_cordz_mean_interval(); + +// Sets the sample rate with the average interval between samples. +void set_cordz_mean_interval(int32_t mean_interval); + +// Cordz is only enabled on Linux with thread_local support. +#if defined(ABSL_INTERNAL_CORDZ_ENABLED) +#error ABSL_INTERNAL_CORDZ_ENABLED cannot be set directly +#elif defined(__linux__) && defined(ABSL_HAVE_THREAD_LOCAL) +#define ABSL_INTERNAL_CORDZ_ENABLED 1 +#endif + +#ifdef ABSL_INTERNAL_CORDZ_ENABLED + +// cordz_next_sample is the number of events until the next sample event. If +// the value is 1 or less, the code will check on the next event if cordz is +// enabled, and if so, will sample the Cord. cordz is only enabled when we can +// use thread locals. +ABSL_CONST_INIT extern thread_local int64_t cordz_next_sample; + +// Determines if the next sample should be profiled. If it is, the value pointed +// at by next_sample will be set with the interval until the next sample. +bool cordz_should_profile_slow(); + +// Returns true if the next cord should be sampled. +inline bool cordz_should_profile() { + if (ABSL_PREDICT_TRUE(cordz_next_sample > 1)) { + cordz_next_sample--; + return false; + } + return cordz_should_profile_slow(); +} + +// Sets the interval until the next sample (for testing only) +void cordz_set_next_sample_for_testing(int64_t next_sample); + +#else // ABSL_INTERNAL_CORDZ_ENABLED + +inline bool cordz_should_profile() { return false; } +inline void cordz_set_next_sample_for_testing(int64_t) {} + +#endif // ABSL_INTERNAL_CORDZ_ENABLED + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_CORDZ_FUNCTIONS_H_ diff --git a/Siv3D/include/ThirdParty/absl/strings/internal/cordz_handle.h b/Siv3D/include/ThirdParty/absl/strings/internal/cordz_handle.h new file mode 100644 index 0000000000..08e3f0d3a6 --- /dev/null +++ b/Siv3D/include/ThirdParty/absl/strings/internal/cordz_handle.h @@ -0,0 +1,98 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_INTERNAL_CORDZ_HANDLE_H_ +#define ABSL_STRINGS_INTERNAL_CORDZ_HANDLE_H_ + +#include +#include + +#include "absl/base/config.h" +#include "absl/base/internal/raw_logging.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// This base class allows multiple types of object (CordzInfo and +// CordzSampleToken) to exist simultaneously on the delete queue (pointed to by +// global_dq_tail and traversed using dq_prev_ and dq_next_). The +// delete queue guarantees that once a profiler creates a CordzSampleToken and +// has gained visibility into a CordzInfo object, that CordzInfo object will not +// be deleted prematurely. This allows the profiler to inspect all CordzInfo +// objects that are alive without needing to hold a global lock. +class ABSL_DLL CordzHandle { + public: + CordzHandle() : CordzHandle(false) {} + + bool is_snapshot() const { return is_snapshot_; } + + // Returns true if this instance is safe to be deleted because it is either a + // snapshot, which is always safe to delete, or not included in the global + // delete queue and thus not included in any snapshot. + // Callers are responsible for making sure this instance can not be newly + // discovered by other threads. For example, CordzInfo instances first de-list + // themselves from the global CordzInfo list before determining if they are + // safe to be deleted directly. + // If SafeToDelete returns false, callers MUST use the Delete() method to + // safely queue CordzHandle instances for deletion. + bool SafeToDelete() const; + + // Deletes the provided instance, or puts it on the delete queue to be deleted + // once there are no more sample tokens (snapshot) instances potentially + // referencing the instance. `handle` should not be null. + static void Delete(CordzHandle* handle); + + // Returns the current entries in the delete queue in LIFO order. + static std::vector DiagnosticsGetDeleteQueue(); + + // Returns true if the provided handle is nullptr or guarded by this handle. + // Since the CordzSnapshot token is itself a CordzHandle, this method will + // allow tests to check if that token is keeping an arbitrary CordzHandle + // alive. + bool DiagnosticsHandleIsSafeToInspect(const CordzHandle* handle) const; + + // Returns the current entries in the delete queue, in LIFO order, that are + // protected by this. CordzHandle objects are only placed on the delete queue + // after CordzHandle::Delete is called with them as an argument. Only + // CordzHandle objects that are not also CordzSnapshot objects will be + // included in the return vector. For each of the handles in the return + // vector, the earliest that their memory can be freed is when this + // CordzSnapshot object is deleted. + std::vector DiagnosticsGetSafeToInspectDeletedHandles(); + + protected: + explicit CordzHandle(bool is_snapshot); + virtual ~CordzHandle(); + + private: + const bool is_snapshot_; + + // dq_prev_ and dq_next_ require the global queue mutex to be held. + // Unfortunately we can't use thread annotations such that the thread safety + // analysis understands that queue_ and global_queue_ are one and the same. + CordzHandle* dq_prev_ = nullptr; + CordzHandle* dq_next_ = nullptr; +}; + +class CordzSnapshot : public CordzHandle { + public: + CordzSnapshot() : CordzHandle(true) {} +}; + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_CORDZ_HANDLE_H_ diff --git a/Siv3D/include/ThirdParty/absl/strings/internal/cordz_info.h b/Siv3D/include/ThirdParty/absl/strings/internal/cordz_info.h new file mode 100644 index 0000000000..17eaa91c77 --- /dev/null +++ b/Siv3D/include/ThirdParty/absl/strings/internal/cordz_info.h @@ -0,0 +1,298 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_INTERNAL_CORDZ_INFO_H_ +#define ABSL_STRINGS_INTERNAL_CORDZ_INFO_H_ + +#include +#include +#include + +#include "absl/base/config.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/base/internal/spinlock.h" +#include "absl/base/thread_annotations.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cordz_functions.h" +#include "absl/strings/internal/cordz_handle.h" +#include "absl/strings/internal/cordz_statistics.h" +#include "absl/strings/internal/cordz_update_tracker.h" +#include "absl/synchronization/mutex.h" +#include "absl/types/span.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// CordzInfo tracks a profiled Cord. Each of these objects can be in two places. +// If a Cord is alive, the CordzInfo will be in the global_cordz_infos map, and +// can also be retrieved via the linked list starting with +// global_cordz_infos_head and continued via the cordz_info_next() method. When +// a Cord has reached the end of its lifespan, the CordzInfo object will be +// migrated out of the global_cordz_infos list and the global_cordz_infos_map, +// and will either be deleted or appended to the global_delete_queue. If it is +// placed on the global_delete_queue, the CordzInfo object will be cleaned in +// the destructor of a CordzSampleToken object. +class ABSL_LOCKABLE CordzInfo : public CordzHandle { + public: + using MethodIdentifier = CordzUpdateTracker::MethodIdentifier; + + // TrackCord creates a CordzInfo instance which tracks important metrics of + // a sampled cord, and stores the created CordzInfo instance into `cord'. All + // CordzInfo instances are placed in a global list which is used to discover + // and snapshot all actively tracked cords. Callers are responsible for + // calling UntrackCord() before the tracked Cord instance is deleted, or to + // stop tracking the sampled Cord. Callers are also responsible for guarding + // changes to the 'tree' value of a Cord (InlineData.tree) through the Lock() + // and Unlock() calls. Any change resulting in a new tree value for the cord + // requires a call to SetCordRep() before the old tree has been unreffed + // and/or deleted. `method` identifies the Cord public API method initiating + // the cord to be sampled. + // Requires `cord` to hold a tree, and `cord.cordz_info()` to be null. + static void TrackCord(InlineData& cord, MethodIdentifier method); + + // Identical to TrackCord(), except that this function fills the + // `parent_stack` and `parent_method` properties of the returned CordzInfo + // instance from the provided `src` instance if `src` is sampled. + // This function should be used for sampling 'copy constructed' and 'copy + // assigned' cords. This function allows 'cord` to be already sampled, in + // which case the CordzInfo will be newly created from `src`. + static void TrackCord(InlineData& cord, const InlineData& src, + MethodIdentifier method); + + // Maybe sample the cord identified by 'cord' for method 'method'. + // Uses `cordz_should_profile` to randomly pick cords to be sampled, and if + // so, invokes `TrackCord` to start sampling `cord`. + static void MaybeTrackCord(InlineData& cord, MethodIdentifier method); + + // Maybe sample the cord identified by 'cord' for method 'method'. + // `src` identifies a 'parent' cord which is assigned to `cord`, typically the + // input cord for a copy constructor, or an assign method such as `operator=` + // `cord` will be sampled if (and only if) `src` is sampled. + // If `cord` is currently being sampled and `src` is not being sampled, then + // this function will stop sampling the cord and reset the cord's cordz_info. + // + // Previously this function defined that `cord` will be sampled if either + // `src` is sampled, or if `cord` is randomly picked for sampling. However, + // this can cause issues, as there may be paths where some cord is assigned an + // indirect copy of it's own value. As such a 'string of copies' would then + // remain sampled (`src.is_profiled`), then assigning such a cord back to + // 'itself' creates a cycle where the cord will converge to 'always sampled`. + // + // For example: + // + // Cord x; + // for (...) { + // // Copy ctor --> y.is_profiled := x.is_profiled | random(...) + // Cord y = x; + // ... + // // Assign x = y --> x.is_profiled = y.is_profiled | random(...) + // // ==> x.is_profiled |= random(...) + // // ==> x converges to 'always profiled' + // x = y; + // } + static void MaybeTrackCord(InlineData& cord, const InlineData& src, + MethodIdentifier method); + + // Stops tracking changes for a sampled cord, and deletes the provided info. + // This function must be called before the sampled cord instance is deleted, + // and before the root cordrep of the sampled cord is unreffed. + // This function may extend the lifetime of the cordrep in cases where the + // CordInfo instance is being held by a concurrent collection thread. + void Untrack(); + + // Invokes UntrackCord() on `info` if `info` is not null. + static void MaybeUntrackCord(CordzInfo* info); + + CordzInfo() = delete; + CordzInfo(const CordzInfo&) = delete; + CordzInfo& operator=(const CordzInfo&) = delete; + + // Retrieves the oldest existing CordzInfo. + static CordzInfo* Head(const CordzSnapshot& snapshot) + ABSL_NO_THREAD_SAFETY_ANALYSIS; + + // Retrieves the next oldest existing CordzInfo older than 'this' instance. + CordzInfo* Next(const CordzSnapshot& snapshot) const + ABSL_NO_THREAD_SAFETY_ANALYSIS; + + // Locks this instance for the update identified by `method`. + // Increases the count for `method` in `update_tracker`. + void Lock(MethodIdentifier method) ABSL_EXCLUSIVE_LOCK_FUNCTION(mutex_); + + // Unlocks this instance. If the contained `rep` has been set to null + // indicating the Cord has been cleared or is otherwise no longer sampled, + // then this method will delete this CordzInfo instance. + void Unlock() ABSL_UNLOCK_FUNCTION(mutex_); + + // Asserts that this CordzInfo instance is locked. + void AssertHeld() ABSL_ASSERT_EXCLUSIVE_LOCK(mutex_); + + // Updates the `rep` property of this instance. This methods is invoked by + // Cord logic each time the root node of a sampled Cord changes, and before + // the old root reference count is deleted. This guarantees that collection + // code can always safely take a reference on the tracked cord. + // Requires a lock to be held through the `Lock()` method. + // TODO(b/117940323): annotate with ABSL_EXCLUSIVE_LOCKS_REQUIRED once all + // Cord code is in a state where this can be proven true by the compiler. + void SetCordRep(CordRep* rep); + + // Returns the current `rep` property of this instance with a reference + // added, or null if this instance represents a cord that has since been + // deleted or untracked. + CordRep* RefCordRep() const ABSL_LOCKS_EXCLUDED(mutex_); + + // Returns the current value of `rep_` for testing purposes only. + CordRep* GetCordRepForTesting() const ABSL_NO_THREAD_SAFETY_ANALYSIS { + return rep_; + } + + // Sets the current value of `rep_` for testing purposes only. + void SetCordRepForTesting(CordRep* rep) ABSL_NO_THREAD_SAFETY_ANALYSIS { + rep_ = rep; + } + + // Returns the stack trace for where the cord was first sampled. Cords are + // potentially sampled when they promote from an inlined cord to a tree or + // ring representation, which is not necessarily the location where the cord + // was first created. Some cords are created as inlined cords, and only as + // data is added do they become a non-inlined cord. However, typically the + // location represents reasonably well where the cord is 'created'. + absl::Span GetStack() const; + + // Returns the stack trace for a sampled cord's 'parent stack trace'. This + // value may be set if the cord is sampled (promoted) after being created + // from, or being assigned the value of an existing (sampled) cord. + absl::Span GetParentStack() const; + + // Retrieves the CordzStatistics associated with this Cord. The statistics + // are only updated when a Cord goes through a mutation, such as an Append + // or RemovePrefix. + CordzStatistics GetCordzStatistics() const; + + private: + using SpinLock = absl::base_internal::SpinLock; + using SpinLockHolder = ::absl::base_internal::SpinLockHolder; + + // Global cordz info list. CordzInfo stores a pointer to the global list + // instance to harden against ODR violations. + struct List { + constexpr explicit List(absl::ConstInitType) + : mutex(absl::kConstInit, + absl::base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL) {} + + SpinLock mutex; + std::atomic head ABSL_GUARDED_BY(mutex){nullptr}; + }; + + static constexpr size_t kMaxStackDepth = 64; + + explicit CordzInfo(CordRep* rep, const CordzInfo* src, + MethodIdentifier method); + ~CordzInfo() override; + + // Sets `rep_` without holding a lock. + void UnsafeSetCordRep(CordRep* rep) ABSL_NO_THREAD_SAFETY_ANALYSIS; + + void Track(); + + // Returns the parent method from `src`, which is either `parent_method_` or + // `method_` depending on `parent_method_` being kUnknown. + // Returns kUnknown if `src` is null. + static MethodIdentifier GetParentMethod(const CordzInfo* src); + + // Fills the provided stack from `src`, copying either `parent_stack_` or + // `stack_` depending on `parent_stack_` being empty, returning the size of + // the parent stack. + // Returns 0 if `src` is null. + static size_t FillParentStack(const CordzInfo* src, void** stack); + + void ODRCheck() const { +#ifndef NDEBUG + ABSL_RAW_CHECK(list_ == &global_list_, "ODR violation in Cord"); +#endif + } + + // Non-inlined implementation of `MaybeTrackCord`, which is executed if + // either `src` is sampled or `cord` is sampled, and either untracks or + // tracks `cord` as documented per `MaybeTrackCord`. + static void MaybeTrackCordImpl(InlineData& cord, const InlineData& src, + MethodIdentifier method); + + ABSL_CONST_INIT static List global_list_; + List* const list_ = &global_list_; + + // ci_prev_ and ci_next_ require the global list mutex to be held. + // Unfortunately we can't use thread annotations such that the thread safety + // analysis understands that list_ and global_list_ are one and the same. + std::atomic ci_prev_{nullptr}; + std::atomic ci_next_{nullptr}; + + mutable absl::Mutex mutex_; + CordRep* rep_ ABSL_GUARDED_BY(mutex_); + + void* stack_[kMaxStackDepth]; + void* parent_stack_[kMaxStackDepth]; + const size_t stack_depth_; + const size_t parent_stack_depth_; + const MethodIdentifier method_; + const MethodIdentifier parent_method_; + CordzUpdateTracker update_tracker_; + const absl::Time create_time_; +}; + +inline ABSL_ATTRIBUTE_ALWAYS_INLINE void CordzInfo::MaybeTrackCord( + InlineData& cord, MethodIdentifier method) { + if (ABSL_PREDICT_FALSE(cordz_should_profile())) { + TrackCord(cord, method); + } +} + +inline ABSL_ATTRIBUTE_ALWAYS_INLINE void CordzInfo::MaybeTrackCord( + InlineData& cord, const InlineData& src, MethodIdentifier method) { + if (ABSL_PREDICT_FALSE(InlineData::is_either_profiled(cord, src))) { + MaybeTrackCordImpl(cord, src, method); + } +} + +inline ABSL_ATTRIBUTE_ALWAYS_INLINE void CordzInfo::MaybeUntrackCord( + CordzInfo* info) { + if (ABSL_PREDICT_FALSE(info)) { + info->Untrack(); + } +} + +inline void CordzInfo::AssertHeld() ABSL_ASSERT_EXCLUSIVE_LOCK(mutex_) { +#ifndef NDEBUG + mutex_.AssertHeld(); +#endif +} + +inline void CordzInfo::SetCordRep(CordRep* rep) { + AssertHeld(); + rep_ = rep; +} + +inline void CordzInfo::UnsafeSetCordRep(CordRep* rep) { rep_ = rep; } + +inline CordRep* CordzInfo::RefCordRep() const ABSL_LOCKS_EXCLUDED(mutex_) { + MutexLock lock(&mutex_); + return rep_ ? CordRep::Ref(rep_) : nullptr; +} + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_CORDZ_INFO_H_ diff --git a/Siv3D/include/ThirdParty/absl/strings/internal/cordz_sample_token.h b/Siv3D/include/ThirdParty/absl/strings/internal/cordz_sample_token.h new file mode 100644 index 0000000000..2a86bc3b14 --- /dev/null +++ b/Siv3D/include/ThirdParty/absl/strings/internal/cordz_sample_token.h @@ -0,0 +1,97 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/base/config.h" +#include "absl/strings/internal/cordz_handle.h" +#include "absl/strings/internal/cordz_info.h" + +#ifndef ABSL_STRINGS_INTERNAL_CORDZ_SAMPLE_TOKEN_H_ +#define ABSL_STRINGS_INTERNAL_CORDZ_SAMPLE_TOKEN_H_ + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// The existence of a CordzSampleToken guarantees that a reader can traverse the +// global_cordz_infos_head linked-list without needing to hold a mutex. When a +// CordzSampleToken exists, all CordzInfo objects that would be destroyed are +// instead appended to a deletion queue. When the CordzSampleToken is destroyed, +// it will also clean up any of these CordzInfo objects. +// +// E.g., ST are CordzSampleToken objects and CH are CordzHandle objects. +// ST1 <- CH1 <- CH2 <- ST2 <- CH3 <- global_delete_queue_tail +// +// This list tracks that CH1 and CH2 were created after ST1, so the thread +// holding ST1 might have a reference to CH1, CH2, ST2, and CH3. However, ST2 +// was created later, so the thread holding the ST2 token cannot have a +// reference to ST1, CH1, or CH2. If ST1 is cleaned up first, that thread will +// delete ST1, CH1, and CH2. If instead ST2 is cleaned up first, that thread +// will only delete ST2. +// +// If ST1 is cleaned up first, the new list will be: +// ST2 <- CH3 <- global_delete_queue_tail +// +// If ST2 is cleaned up first, the new list will be: +// ST1 <- CH1 <- CH2 <- CH3 <- global_delete_queue_tail +// +// All new CordzHandle objects are appended to the list, so if a new thread +// comes along before either ST1 or ST2 are cleaned up, the new list will be: +// ST1 <- CH1 <- CH2 <- ST2 <- CH3 <- ST3 <- global_delete_queue_tail +// +// A thread must hold the global_delete_queue_mu mutex whenever it's altering +// this list. +// +// It is safe for thread that holds a CordzSampleToken to read +// global_cordz_infos at any time since the objects it is able to retrieve will +// not be deleted while the CordzSampleToken exists. +class CordzSampleToken : public CordzSnapshot { + public: + class Iterator { + public: + using iterator_category = std::input_iterator_tag; + using value_type = const CordzInfo&; + using difference_type = ptrdiff_t; + using pointer = const CordzInfo*; + using reference = value_type; + + Iterator() = default; + + Iterator& operator++(); + Iterator operator++(int); + friend bool operator==(const Iterator& lhs, const Iterator& rhs); + friend bool operator!=(const Iterator& lhs, const Iterator& rhs); + reference operator*() const; + pointer operator->() const; + + private: + friend class CordzSampleToken; + explicit Iterator(const CordzSampleToken* token); + + const CordzSampleToken* token_ = nullptr; + pointer current_ = nullptr; + }; + + CordzSampleToken() = default; + CordzSampleToken(const CordzSampleToken&) = delete; + CordzSampleToken& operator=(const CordzSampleToken&) = delete; + + Iterator begin() { return Iterator(this); } + Iterator end() { return Iterator(); } +}; + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_CORDZ_SAMPLE_TOKEN_H_ diff --git a/Siv3D/include/ThirdParty/absl/strings/internal/cordz_statistics.h b/Siv3D/include/ThirdParty/absl/strings/internal/cordz_statistics.h new file mode 100644 index 0000000000..9f558df494 --- /dev/null +++ b/Siv3D/include/ThirdParty/absl/strings/internal/cordz_statistics.h @@ -0,0 +1,88 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_INTERNAL_CORDZ_STATISTICS_H_ +#define ABSL_STRINGS_INTERNAL_CORDZ_STATISTICS_H_ + +#include + +#include "absl/base/config.h" +#include "absl/strings/internal/cordz_update_tracker.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// CordzStatistics captures some meta information about a Cord's shape. +struct CordzStatistics { + using MethodIdentifier = CordzUpdateTracker::MethodIdentifier; + + // Node counts information + struct NodeCounts { + size_t flat = 0; // #flats + size_t flat_64 = 0; // #flats up to 64 bytes + size_t flat_128 = 0; // #flats up to 128 bytes + size_t flat_256 = 0; // #flats up to 256 bytes + size_t flat_512 = 0; // #flats up to 512 bytes + size_t flat_1k = 0; // #flats up to 1K bytes + size_t external = 0; // #external reps + size_t substring = 0; // #substring reps + size_t concat = 0; // #concat reps + size_t ring = 0; // #ring buffer reps + size_t btree = 0; // #btree reps + size_t crc = 0; // #crc reps + }; + + // The size of the cord in bytes. This matches the result of Cord::size(). + size_t size = 0; + + // The estimated memory used by the sampled cord. This value matches the + // value as reported by Cord::EstimatedMemoryUsage(). + // A value of 0 implies the property has not been recorded. + size_t estimated_memory_usage = 0; + + // The effective memory used by the sampled cord, inversely weighted by the + // effective indegree of each allocated node. This is a representation of the + // fair share of memory usage that should be attributed to the sampled cord. + // This value is more useful for cases where one or more nodes are referenced + // by multiple Cord instances, and for cases where a Cord includes the same + // node multiple times (either directly or indirectly). + // A value of 0 implies the property has not been recorded. + size_t estimated_fair_share_memory_usage = 0; + + // The total number of nodes referenced by this cord. + // For ring buffer Cords, this includes the 'ring buffer' node. + // For btree Cords, this includes all 'CordRepBtree' tree nodes as well as all + // the substring, flat and external nodes referenced by the tree. + // A value of 0 implies the property has not been recorded. + size_t node_count = 0; + + // Detailed node counts per type + NodeCounts node_counts; + + // The cord method responsible for sampling the cord. + MethodIdentifier method = MethodIdentifier::kUnknown; + + // The cord method responsible for sampling the parent cord if applicable. + MethodIdentifier parent_method = MethodIdentifier::kUnknown; + + // Update tracker tracking invocation count per cord method. + CordzUpdateTracker update_tracker; +}; + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_CORDZ_STATISTICS_H_ diff --git a/Siv3D/include/ThirdParty/absl/strings/internal/cordz_update_scope.h b/Siv3D/include/ThirdParty/absl/strings/internal/cordz_update_scope.h new file mode 100644 index 0000000000..57ba75de93 --- /dev/null +++ b/Siv3D/include/ThirdParty/absl/strings/internal/cordz_update_scope.h @@ -0,0 +1,71 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_INTERNAL_CORDZ_UPDATE_SCOPE_H_ +#define ABSL_STRINGS_INTERNAL_CORDZ_UPDATE_SCOPE_H_ + +#include "absl/base/config.h" +#include "absl/base/optimization.h" +#include "absl/base/thread_annotations.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cordz_info.h" +#include "absl/strings/internal/cordz_update_tracker.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// CordzUpdateScope scopes an update to the provided CordzInfo. +// The class invokes `info->Lock(method)` and `info->Unlock()` to guard +// cordrep updates. This class does nothing if `info` is null. +// See also the 'Lock`, `Unlock` and `SetCordRep` methods in `CordzInfo`. +class ABSL_SCOPED_LOCKABLE CordzUpdateScope { + public: + CordzUpdateScope(CordzInfo* info, CordzUpdateTracker::MethodIdentifier method) + ABSL_EXCLUSIVE_LOCK_FUNCTION(info) + : info_(info) { + if (ABSL_PREDICT_FALSE(info_)) { + info->Lock(method); + } + } + + // CordzUpdateScope can not be copied or assigned to. + CordzUpdateScope(CordzUpdateScope&& rhs) = delete; + CordzUpdateScope(const CordzUpdateScope&) = delete; + CordzUpdateScope& operator=(CordzUpdateScope&& rhs) = delete; + CordzUpdateScope& operator=(const CordzUpdateScope&) = delete; + + ~CordzUpdateScope() ABSL_UNLOCK_FUNCTION() { + if (ABSL_PREDICT_FALSE(info_)) { + info_->Unlock(); + } + } + + void SetCordRep(CordRep* rep) const { + if (ABSL_PREDICT_FALSE(info_)) { + info_->SetCordRep(rep); + } + } + + CordzInfo* info() const { return info_; } + + private: + CordzInfo* info_; +}; + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_CORDZ_UPDATE_SCOPE_H_ diff --git a/Siv3D/include/ThirdParty/absl/strings/internal/cordz_update_tracker.h b/Siv3D/include/ThirdParty/absl/strings/internal/cordz_update_tracker.h new file mode 100644 index 0000000000..c5170662bf --- /dev/null +++ b/Siv3D/include/ThirdParty/absl/strings/internal/cordz_update_tracker.h @@ -0,0 +1,123 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_INTERNAL_CORDZ_UPDATE_TRACKER_H_ +#define ABSL_STRINGS_INTERNAL_CORDZ_UPDATE_TRACKER_H_ + +#include +#include + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// CordzUpdateTracker tracks counters for Cord update methods. +// +// The purpose of CordzUpdateTracker is to track the number of calls to methods +// updating Cord data for sampled cords. The class internally uses 'lossy' +// atomic operations: Cord is thread-compatible, so there is no need to +// synchronize updates. However, Cordz collection threads may call 'Value()' at +// any point, so the class needs to provide thread safe access. +// +// This class is thread-safe. But as per above comments, all non-const methods +// should be used single-threaded only: updates are thread-safe but lossy. +class CordzUpdateTracker { + public: + // Tracked update methods. + enum MethodIdentifier { + kUnknown, + kAppendCord, + kAppendCordBuffer, + kAppendExternalMemory, + kAppendString, + kAssignCord, + kAssignString, + kClear, + kConstructorCord, + kConstructorString, + kCordReader, + kFlatten, + kGetAppendBuffer, + kGetAppendRegion, + kMakeCordFromExternal, + kMoveAppendCord, + kMoveAssignCord, + kMovePrependCord, + kPrependCord, + kPrependCordBuffer, + kPrependString, + kRemovePrefix, + kRemoveSuffix, + kSetExpectedChecksum, + kSubCord, + + // kNumMethods defines the number of entries: must be the last entry. + kNumMethods, + }; + + // Constructs a new instance. All counters are zero-initialized. + constexpr CordzUpdateTracker() noexcept : values_{} {} + + // Copy constructs a new instance. + CordzUpdateTracker(const CordzUpdateTracker& rhs) noexcept { *this = rhs; } + + // Assigns the provided value to this instance. + CordzUpdateTracker& operator=(const CordzUpdateTracker& rhs) noexcept { + for (int i = 0; i < kNumMethods; ++i) { + values_[i].store(rhs.values_[i].load(std::memory_order_relaxed), + std::memory_order_relaxed); + } + return *this; + } + + // Returns the value for the specified method. + int64_t Value(MethodIdentifier method) const { + return values_[method].load(std::memory_order_relaxed); + } + + // Increases the value for the specified method by `n` + void LossyAdd(MethodIdentifier method, int64_t n = 1) { + auto& value = values_[method]; + value.store(value.load(std::memory_order_relaxed) + n, + std::memory_order_relaxed); + } + + // Adds all the values from `src` to this instance + void LossyAdd(const CordzUpdateTracker& src) { + for (int i = 0; i < kNumMethods; ++i) { + MethodIdentifier method = static_cast(i); + if (int64_t value = src.Value(method)) { + LossyAdd(method, value); + } + } + } + + private: + // Until C++20 std::atomic is not constexpr default-constructible, so we need + // a wrapper for this class to be constexpr constructible. + class Counter : public std::atomic { + public: + constexpr Counter() noexcept : std::atomic(0) {} + }; + + Counter values_[kNumMethods]; +}; + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_CORDZ_UPDATE_TRACKER_H_ diff --git a/Siv3D/include/ThirdParty/absl/strings/internal/damerau_levenshtein_distance.h b/Siv3D/include/ThirdParty/absl/strings/internal/damerau_levenshtein_distance.h new file mode 100644 index 0000000000..7a4bd64462 --- /dev/null +++ b/Siv3D/include/ThirdParty/absl/strings/internal/damerau_levenshtein_distance.h @@ -0,0 +1,34 @@ +// Copyright 2022 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_INTERNAL_DAMERAU_LEVENSHTEIN_DISTANCE_H_ +#define ABSL_STRINGS_INTERNAL_DAMERAU_LEVENSHTEIN_DISTANCE_H_ + +#include + +#include "absl/strings/string_view.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace strings_internal { +// Calculate DamerauLevenshtein distance between two strings. +// When the distance is larger than cutoff, the code just returns cutoff + 1. +uint8_t CappedDamerauLevenshteinDistance(absl::string_view s1, + absl::string_view s2, uint8_t cutoff); + +} // namespace strings_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_DAMERAU_LEVENSHTEIN_DISTANCE_H_ diff --git a/Siv3D/include/ThirdParty/absl/strings/internal/escaping.h b/Siv3D/include/ThirdParty/absl/strings/internal/escaping.h index 6a9ce602d9..2186f7787d 100644 --- a/Siv3D/include/ThirdParty/absl/strings/internal/escaping.h +++ b/Siv3D/include/ThirdParty/absl/strings/internal/escaping.h @@ -24,20 +24,19 @@ ABSL_NAMESPACE_BEGIN namespace strings_internal { ABSL_CONST_INIT extern const char kBase64Chars[]; +ABSL_CONST_INIT extern const char kWebSafeBase64Chars[]; -// Calculates how long a string will be when it is base64 encoded given its -// length and whether or not the result should be padded. +// Calculates the length of a Base64 encoding (RFC 4648) of a string of length +// `input_len`, with or without padding per `do_padding`. Note that 'web-safe' +// encoding (section 5 of the RFC) does not change this length. size_t CalculateBase64EscapedLenInternal(size_t input_len, bool do_padding); -// Base64-encodes `src` using the alphabet provided in `base64` and writes the -// result to `dest`. If `do_padding` is true, `dest` is padded with '=' chars -// until its length is a multiple of 3. Returns the length of `dest`. +// Base64-encodes `src` using the alphabet provided in `base64` (which +// determines whether to do web-safe encoding or not) and writes the result to +// `dest`. If `do_padding` is true, `dest` is padded with '=' chars until its +// length is a multiple of 3. Returns the length of `dest`. size_t Base64EscapeInternal(const unsigned char* src, size_t szsrc, char* dest, size_t szdest, const char* base64, bool do_padding); - -// Base64-encodes `src` using the alphabet provided in `base64` and writes the -// result to `dest`. If `do_padding` is true, `dest` is padded with '=' chars -// until its length is a multiple of 3. template void Base64EscapeInternal(const unsigned char* src, size_t szsrc, String* dest, bool do_padding, const char* base64_chars) { diff --git a/Siv3D/include/ThirdParty/absl/strings/internal/has_absl_stringify.h b/Siv3D/include/ThirdParty/absl/strings/internal/has_absl_stringify.h new file mode 100644 index 0000000000..55a0850829 --- /dev/null +++ b/Siv3D/include/ThirdParty/absl/strings/internal/has_absl_stringify.h @@ -0,0 +1,55 @@ +// Copyright 2022 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_INTERNAL_HAS_ABSL_STRINGIFY_H_ +#define ABSL_STRINGS_INTERNAL_HAS_ABSL_STRINGIFY_H_ +#include +#include +#include + +#include "absl/strings/string_view.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +namespace strings_internal { + +// This is an empty class not intended to be used. It exists so that +// `HasAbslStringify` can reference a universal class rather than needing to be +// copied for each new sink. +class UnimplementedSink { + public: + void Append(size_t count, char ch); + + void Append(string_view v); + + // Support `absl::Format(&sink, format, args...)`. + friend void AbslFormatFlush(UnimplementedSink* sink, absl::string_view v); +}; + +template +struct HasAbslStringify : std::false_type {}; + +template +struct HasAbslStringify< + T, std::enable_if_t(), + std::declval()))>::value>> : std::true_type {}; + +} // namespace strings_internal + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_HAS_ABSL_STRINGIFY_H_ diff --git a/Siv3D/include/ThirdParty/absl/strings/internal/memutil.h b/Siv3D/include/ThirdParty/absl/strings/internal/memutil.h index 9ad0535808..b5911a01cd 100644 --- a/Siv3D/include/ThirdParty/absl/strings/internal/memutil.h +++ b/Siv3D/include/ThirdParty/absl/strings/internal/memutil.h @@ -14,51 +14,6 @@ // limitations under the License. // -// These routines provide mem versions of standard C string routines, -// such as strpbrk. They function exactly the same as the str versions, -// so if you wonder what they are, replace the word "mem" by -// "str" and check out the man page. I could return void*, as the -// strutil.h mem*() routines tend to do, but I return char* instead -// since this is by far the most common way these functions are called. -// -// The difference between the mem and str versions is the mem version -// takes a pointer and a length, rather than a '\0'-terminated string. -// The memcase* routines defined here assume the locale is "C" -// (they use absl::ascii_tolower instead of tolower). -// -// These routines are based on the BSD library. -// -// Here's a list of routines from string.h, and their mem analogues. -// Functions in lowercase are defined in string.h; those in UPPERCASE -// are defined here: -// -// strlen -- -// strcat strncat MEMCAT -// strcpy strncpy memcpy -// -- memccpy (very cool function, btw) -// -- memmove -// -- memset -// strcmp strncmp memcmp -// strcasecmp strncasecmp MEMCASECMP -// strchr memchr -// strcoll -- -// strxfrm -- -// strdup strndup MEMDUP -// strrchr MEMRCHR -// strspn MEMSPN -// strcspn MEMCSPN -// strpbrk MEMPBRK -// strstr MEMSTR MEMMEM -// (g)strcasestr MEMCASESTR MEMCASEMEM -// strtok -- -// strprefix MEMPREFIX (strprefix is from strutil.h) -// strcaseprefix MEMCASEPREFIX (strcaseprefix is from strutil.h) -// strsuffix MEMSUFFIX (strsuffix is from strutil.h) -// strcasesuffix MEMCASESUFFIX (strcasesuffix is from strutil.h) -// -- MEMIS -// -- MEMCASEIS -// strcount MEMCOUNT (strcount is from strutil.h) - #ifndef ABSL_STRINGS_INTERNAL_MEMUTIL_H_ #define ABSL_STRINGS_INTERNAL_MEMUTIL_H_ @@ -72,74 +27,11 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace strings_internal { -inline char* memcat(char* dest, size_t destlen, const char* src, - size_t srclen) { - return reinterpret_cast(memcpy(dest + destlen, src, srclen)); -} - +// Performs a byte-by-byte comparison of `len` bytes of the strings `s1` and +// `s2`, ignoring the case of the characters. It returns an integer less than, +// equal to, or greater than zero if `s1` is found, respectively, to be less +// than, to match, or be greater than `s2`. int memcasecmp(const char* s1, const char* s2, size_t len); -char* memdup(const char* s, size_t slen); -char* memrchr(const char* s, int c, size_t slen); -size_t memspn(const char* s, size_t slen, const char* accept); -size_t memcspn(const char* s, size_t slen, const char* reject); -char* mempbrk(const char* s, size_t slen, const char* accept); - -// This is for internal use only. Don't call this directly -template -const char* int_memmatch(const char* haystack, size_t haylen, - const char* needle, size_t neelen) { - if (0 == neelen) { - return haystack; // even if haylen is 0 - } - const char* hayend = haystack + haylen; - const char* needlestart = needle; - const char* needleend = needlestart + neelen; - - for (; haystack < hayend; ++haystack) { - char hay = case_sensitive - ? *haystack - : absl::ascii_tolower(static_cast(*haystack)); - char nee = case_sensitive - ? *needle - : absl::ascii_tolower(static_cast(*needle)); - if (hay == nee) { - if (++needle == needleend) { - return haystack + 1 - neelen; - } - } else if (needle != needlestart) { - // must back up haystack in case a prefix matched (find "aab" in "aaab") - haystack -= needle - needlestart; // for loop will advance one more - needle = needlestart; - } - } - return nullptr; -} - -// These are the guys you can call directly -inline const char* memstr(const char* phaystack, size_t haylen, - const char* pneedle) { - return int_memmatch(phaystack, haylen, pneedle, strlen(pneedle)); -} - -inline const char* memcasestr(const char* phaystack, size_t haylen, - const char* pneedle) { - return int_memmatch(phaystack, haylen, pneedle, strlen(pneedle)); -} - -inline const char* memmem(const char* phaystack, size_t haylen, - const char* pneedle, size_t needlelen) { - return int_memmatch(phaystack, haylen, pneedle, needlelen); -} - -inline const char* memcasemem(const char* phaystack, size_t haylen, - const char* pneedle, size_t needlelen) { - return int_memmatch(phaystack, haylen, pneedle, needlelen); -} - -// This is significantly faster for case-sensitive matches with very -// few possible matches. See unit test for benchmarks. -const char* memmatch(const char* phaystack, size_t haylen, const char* pneedle, - size_t neelen); } // namespace strings_internal ABSL_NAMESPACE_END diff --git a/Siv3D/include/ThirdParty/absl/strings/internal/ostringstream.h b/Siv3D/include/ThirdParty/absl/strings/internal/ostringstream.h index d25d60473f..c0e237dbe8 100644 --- a/Siv3D/include/ThirdParty/absl/strings/internal/ostringstream.h +++ b/Siv3D/include/ThirdParty/absl/strings/internal/ostringstream.h @@ -16,11 +16,13 @@ #define ABSL_STRINGS_INTERNAL_OSTRINGSTREAM_H_ #include +#include #include #include #include +#include -#include "absl/base/port.h" +#include "absl/base/config.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -60,26 +62,49 @@ namespace strings_internal { // strm << 3.14; // // Note: flush() has no effect. No reason to call it. -class OStringStream : private std::basic_streambuf, public std::ostream { +class OStringStream final : public std::ostream { public: // The argument can be null, in which case you'll need to call str(p) with a // non-null argument before you can write to the stream. // // The destructor of OStringStream doesn't use the std::string. It's OK to // destroy the std::string before the stream. - explicit OStringStream(std::string* s) : std::ostream(this), s_(s) {} + explicit OStringStream(std::string* str) + : std::ostream(&buf_), buf_(str) {} + OStringStream(OStringStream&& that) + : std::ostream(std::move(static_cast(that))), + buf_(that.buf_) { + rdbuf(&buf_); + } + OStringStream& operator=(OStringStream&& that) { + std::ostream::operator=(std::move(static_cast(that))); + buf_ = that.buf_; + rdbuf(&buf_); + return *this; + } - std::string* str() { return s_; } - const std::string* str() const { return s_; } - void str(std::string* s) { s_ = s; } + std::string* str() { return buf_.str(); } + const std::string* str() const { return buf_.str(); } + void str(std::string* str) { buf_.str(str); } private: - using Buf = std::basic_streambuf; + class Streambuf final : public std::streambuf { + public: + explicit Streambuf(std::string* str) : str_(str) {} + Streambuf(const Streambuf&) = default; + Streambuf& operator=(const Streambuf&) = default; - Buf::int_type overflow(int c) override; - std::streamsize xsputn(const char* s, std::streamsize n) override; + std::string* str() { return str_; } + const std::string* str() const { return str_; } + void str(std::string* str) { str_ = str; } - std::string* s_; + protected: + int_type overflow(int c) override; + std::streamsize xsputn(const char* s, std::streamsize n) override; + + private: + std::string* str_; + } buf_; }; } // namespace strings_internal diff --git a/Siv3D/include/ThirdParty/absl/strings/internal/resize_uninitialized.h b/Siv3D/include/ThirdParty/absl/strings/internal/resize_uninitialized.h index e42628e394..49859dcc7d 100644 --- a/Siv3D/include/ThirdParty/absl/strings/internal/resize_uninitialized.h +++ b/Siv3D/include/ThirdParty/absl/strings/internal/resize_uninitialized.h @@ -17,6 +17,7 @@ #ifndef ABSL_STRINGS_INTERNAL_RESIZE_UNINITIALIZED_H_ #define ABSL_STRINGS_INTERNAL_RESIZE_UNINITIALIZED_H_ +#include #include #include #include @@ -28,8 +29,9 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace strings_internal { -// Is a subclass of true_type or false_type, depending on whether or not -// T has a __resize_default_init member. +// In this type trait, we look for a __resize_default_init member function, and +// we use it if available, otherwise, we use resize. We provide HasMember to +// indicate whether __resize_default_init is present. template struct ResizeUninitializedTraits { using HasMember = std::false_type; @@ -66,6 +68,50 @@ inline void STLStringResizeUninitialized(string_type* s, size_t new_size) { ResizeUninitializedTraits::Resize(s, new_size); } +// Used to ensure exponential growth so that the amortized complexity of +// increasing the string size by a small amount is O(1), in contrast to +// O(str->size()) in the case of precise growth. +template +void STLStringReserveAmortized(string_type* s, size_t new_size) { + const size_t cap = s->capacity(); + if (new_size > cap) { + // Make sure to always grow by at least a factor of 2x. + s->reserve((std::max)(new_size, 2 * cap)); + } +} + +// In this type trait, we look for an __append_default_init member function, and +// we use it if available, otherwise, we use append. +template +struct AppendUninitializedTraits { + static void Append(string_type* s, size_t n) { + s->append(n, typename string_type::value_type()); + } +}; + +template +struct AppendUninitializedTraits< + string_type, absl::void_t() + .__append_default_init(237))> > { + static void Append(string_type* s, size_t n) { + s->__append_default_init(n); + } +}; + +// Like STLStringResizeUninitialized(str, new_size), except guaranteed to use +// exponential growth so that the amortized complexity of increasing the string +// size by a small amount is O(1), in contrast to O(str->size()) in the case of +// precise growth. +template +void STLStringResizeUninitializedAmortized(string_type* s, size_t new_size) { + const size_t size = s->size(); + if (new_size > size) { + AppendUninitializedTraits::Append(s, new_size - size); + } else { + s->erase(new_size); + } +} + } // namespace strings_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/Siv3D/include/ThirdParty/absl/strings/internal/stl_type_traits.h b/Siv3D/include/ThirdParty/absl/strings/internal/stl_type_traits.h index 6035ca45cb..e50468b0dc 100644 --- a/Siv3D/include/ThirdParty/absl/strings/internal/stl_type_traits.h +++ b/Siv3D/include/ThirdParty/absl/strings/internal/stl_type_traits.h @@ -13,7 +13,7 @@ // limitations under the License. // -// Thie file provides the IsStrictlyBaseOfAndConvertibleToSTLContainer type +// The file provides the IsStrictlyBaseOfAndConvertibleToSTLContainer type // trait metafunction to assist in working with the _GLIBCXX_DEBUG debug // wrappers of STL containers. // diff --git a/Siv3D/include/ThirdParty/absl/strings/internal/str_format/arg.h b/Siv3D/include/ThirdParty/absl/strings/internal/str_format/arg.h index 3dbc1526d0..3ce30feb49 100644 --- a/Siv3D/include/ThirdParty/absl/strings/internal/str_format/arg.h +++ b/Siv3D/include/ThirdParty/absl/strings/internal/str_format/arg.h @@ -1,9 +1,24 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_ARG_H_ #define ABSL_STRINGS_INTERNAL_STR_FORMAT_ARG_H_ #include #include +#include #include #include #include @@ -11,10 +26,12 @@ #include #include #include +#include #include "absl/base/port.h" #include "absl/meta/type_traits.h" #include "absl/numeric/int128.h" +#include "absl/strings/internal/has_absl_stringify.h" #include "absl/strings/internal/str_format/extension.h" #include "absl/strings/string_view.h" @@ -31,6 +48,24 @@ class FormatConversionSpec; namespace str_format_internal { +template +struct ArgConvertResult { + bool value; +}; + +using IntegralConvertResult = ArgConvertResult; +using FloatingConvertResult = ArgConvertResult; +using CharConvertResult = ArgConvertResult; + template struct HasUserDefinedConvert : std::false_type {}; @@ -41,7 +76,50 @@ struct HasUserDefinedConvert()))>> : std::true_type {}; -void AbslFormatConvert(); // Stops the lexical name lookup +// These declarations prevent ADL lookup from continuing in absl namespaces, +// we are deliberately using these as ADL hooks and want them to consider +// non-absl namespaces only. +void AbslFormatConvert(); +void AbslStringify(); + +template +bool ConvertIntArg(T v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); + +// Forward declarations of internal `ConvertIntArg` function template +// instantiations are here to avoid including the template body in the headers +// and instantiating it in large numbers of translation units. Explicit +// instantiations can be found in "absl/strings/internal/str_format/arg.cc" +extern template bool ConvertIntArg(char v, FormatConversionSpecImpl conv, + FormatSinkImpl* sink); +extern template bool ConvertIntArg(signed char v, + FormatConversionSpecImpl conv, + FormatSinkImpl* sink); +extern template bool ConvertIntArg(unsigned char v, + FormatConversionSpecImpl conv, + FormatSinkImpl* sink); +extern template bool ConvertIntArg(short v, // NOLINT + FormatConversionSpecImpl conv, + FormatSinkImpl* sink); +extern template bool ConvertIntArg( // NOLINT + unsigned short v, FormatConversionSpecImpl conv, // NOLINT + FormatSinkImpl* sink); +extern template bool ConvertIntArg(int v, FormatConversionSpecImpl conv, + FormatSinkImpl* sink); +extern template bool ConvertIntArg(unsigned int v, + FormatConversionSpecImpl conv, + FormatSinkImpl* sink); +extern template bool ConvertIntArg( // NOLINT + long v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); // NOLINT +extern template bool ConvertIntArg(unsigned long v, // NOLINT + FormatConversionSpecImpl conv, + FormatSinkImpl* sink); +extern template bool ConvertIntArg(long long v, // NOLINT + FormatConversionSpecImpl conv, + FormatSinkImpl* sink); +extern template bool ConvertIntArg( // NOLINT + unsigned long long v, FormatConversionSpecImpl conv, // NOLINT + FormatSinkImpl* sink); + template auto FormatConvertImpl(const T& v, FormatConversionSpecImpl conv, FormatSinkImpl* sink) @@ -57,6 +135,39 @@ auto FormatConvertImpl(const T& v, FormatConversionSpecImpl conv, return AbslFormatConvert(v, fcs, &fs); } +template +auto FormatConvertImpl(const T& v, FormatConversionSpecImpl conv, + FormatSinkImpl* sink) + -> std::enable_if_t::value && + std::is_void(), v))>::value, + IntegralConvertResult> { + if (conv.conversion_char() == FormatConversionCharInternal::v) { + using FormatSinkT = + absl::enable_if_t; + auto fs = sink->Wrap(); + AbslStringify(fs, v); + return {true}; + } else { + return {ConvertIntArg( + static_cast::type>(v), conv, sink)}; + } +} + +template +auto FormatConvertImpl(const T& v, FormatConversionSpecImpl, + FormatSinkImpl* sink) + -> std::enable_if_t::value && + std::is_void(), v))>::value, + ArgConvertResult> { + using FormatSinkT = + absl::enable_if_t; + auto fs = sink->Wrap(); + AbslStringify(fs, v); + return {true}; +} + template class StreamedWrapper; @@ -81,11 +192,6 @@ struct VoidPtr { uintptr_t value; }; -template -struct ArgConvertResult { - bool value; -}; - template constexpr FormatConversionCharSet ExtractCharSet(FormatConvertResult) { return C; @@ -96,8 +202,8 @@ constexpr FormatConversionCharSet ExtractCharSet(ArgConvertResult) { return C; } -using StringConvertResult = - ArgConvertResult; +using StringConvertResult = ArgConvertResult; ArgConvertResult FormatConvertImpl( VoidPtr v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); @@ -108,6 +214,14 @@ StringConvertResult FormatConvertImpl(const std::string& v, StringConvertResult FormatConvertImpl(string_view v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); +#if defined(ABSL_HAVE_STD_STRING_VIEW) && !defined(ABSL_USES_STD_STRING_VIEW) +inline StringConvertResult FormatConvertImpl(std::string_view v, + FormatConversionSpecImpl conv, + FormatSinkImpl* sink) { + return FormatConvertImpl(absl::string_view(v.data(), v.size()), conv, sink); +} +#endif // ABSL_HAVE_STD_STRING_VIEW && !ABSL_USES_STD_STRING_VIEW + ArgConvertResult FormatConvertImpl(const char* v, const FormatConversionSpecImpl conv, @@ -122,7 +236,7 @@ StringConvertResult FormatConvertImpl(const AbslCord& value, size_t space_remaining = 0; int width = conv.width(); - if (width >= 0) space_remaining = width; + if (width >= 0) space_remaining = static_cast(width); size_t to_write = value.size(); @@ -151,12 +265,7 @@ StringConvertResult FormatConvertImpl(const AbslCord& value, return {true}; } -using IntegralConvertResult = ArgConvertResult; -using FloatingConvertResult = - ArgConvertResult; +bool ConvertBoolArg(bool v, FormatSinkImpl* sink); // Floats. FloatingConvertResult FormatConvertImpl(float v, FormatConversionSpecImpl conv, @@ -168,16 +277,16 @@ FloatingConvertResult FormatConvertImpl(long double v, FormatSinkImpl* sink); // Chars. -IntegralConvertResult FormatConvertImpl(char v, FormatConversionSpecImpl conv, - FormatSinkImpl* sink); +CharConvertResult FormatConvertImpl(char v, FormatConversionSpecImpl conv, + FormatSinkImpl* sink); + +// Ints. IntegralConvertResult FormatConvertImpl(signed char v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); IntegralConvertResult FormatConvertImpl(unsigned char v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); - -// Ints. IntegralConvertResult FormatConvertImpl(short v, // NOLINT FormatConversionSpecImpl conv, FormatSinkImpl* sink); @@ -206,9 +315,16 @@ IntegralConvertResult FormatConvertImpl(int128 v, FormatConversionSpecImpl conv, IntegralConvertResult FormatConvertImpl(uint128 v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); + +// This function needs to be a template due to ambiguity regarding type +// conversions. template ::value, int> = 0> IntegralConvertResult FormatConvertImpl(T v, FormatConversionSpecImpl conv, FormatSinkImpl* sink) { + if (conv.conversion_char() == FormatConversionCharInternal::v) { + return {ConvertBoolArg(v, sink)}; + } + return FormatConvertImpl(static_cast(v), conv, sink); } @@ -216,7 +332,8 @@ IntegralConvertResult FormatConvertImpl(T v, FormatConversionSpecImpl conv, // FormatArgImpl will use the underlying Convert functions instead. template typename std::enable_if::value && - !HasUserDefinedConvert::value, + !HasUserDefinedConvert::value && + !strings_internal::HasAbslStringify::value, IntegralConvertResult>::type FormatConvertImpl(T v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); @@ -279,11 +396,11 @@ struct FormatArgImplFriend { template constexpr FormatConversionCharSet ArgumentToConv() { - return absl::str_format_internal::ExtractCharSet( - decltype(str_format_internal::FormatConvertImpl( - std::declval(), - std::declval(), - std::declval())){}); + using ConvResult = decltype(str_format_internal::FormatConvertImpl( + std::declval(), + std::declval(), + std::declval())); + return absl::str_format_internal::ExtractCharSet(ConvResult{}); } // A type-erased handle to a format argument. @@ -324,12 +441,13 @@ class FormatArgImpl { // For everything else: // - Decay char* and char arrays into `const char*` // - Decay any other pointer to `const void*` - // - Decay all enums to their underlying type. + // - Decay all enums to the integral promotion of their underlying type. // - Decay function pointers to void*. template struct DecayType { static constexpr bool kHasUserDefined = - str_format_internal::HasUserDefinedConvert::value; + str_format_internal::HasUserDefinedConvert::value || + strings_internal::HasAbslStringify::value; using type = typename std::conditional< !kHasUserDefined && std::is_convertible::value, const char*, @@ -341,8 +459,9 @@ class FormatArgImpl { struct DecayType::value && + !strings_internal::HasAbslStringify::value && std::is_enum::value>::type> { - using type = typename std::underlying_type::type; + using type = decltype(+typename std::underlying_type::type()); }; public: diff --git a/Siv3D/include/ThirdParty/absl/strings/internal/str_format/bind.h b/Siv3D/include/ThirdParty/absl/strings/internal/str_format/bind.h index 585246e77e..5e2a43d5c9 100644 --- a/Siv3D/include/ThirdParty/absl/strings/internal/str_format/bind.h +++ b/Siv3D/include/ThirdParty/absl/strings/internal/str_format/bind.h @@ -1,3 +1,17 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_BIND_H_ #define ABSL_STRINGS_INTERNAL_STR_FORMAT_BIND_H_ @@ -7,10 +21,12 @@ #include #include "absl/base/port.h" +#include "absl/container/inlined_vector.h" #include "absl/strings/internal/str_format/arg.h" #include "absl/strings/internal/str_format/checker.h" #include "absl/strings/internal/str_format/parser.h" #include "absl/types/span.h" +#include "absl/utility/utility.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -73,6 +89,36 @@ class FormatSpecTemplate : public MakeDependent::type { using Base = typename MakeDependent::type; + template + struct ErrorMaker { + constexpr bool operator()(int) const { return res; } + }; + + template + static constexpr bool CheckArity(ErrorMaker SpecifierCount = {}, + ErrorMaker ParametersPassed = {}) { + static_assert(SpecifierCount(i) == ParametersPassed(j), + "Number of arguments passed must match the number of " + "conversion specifiers."); + return true; + } + + template + static constexpr bool CheckMatch( + ErrorMaker MismatchedArgumentNumber = {}) { + static_assert(MismatchedArgumentNumber(arg), + "Passed argument must match specified format."); + return true; + } + + template + static bool CheckMatches(absl::index_sequence) { + bool res[] = {true, CheckMatch()...}; + (void)res; + return true; + } + public: #ifdef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER @@ -86,7 +132,7 @@ class FormatSpecTemplate // We use the 'unavailable' attribute to give a better compiler error than // just 'method is deleted'. // To avoid checking the format twice, we just check that the format is - // constexpr. If is it valid, then the overload below will kick in. + // constexpr. If it is valid, then the overload below will kick in. // We add the template here to make this overload have lower priority. template FormatSpecTemplate(const char* s) // NOLINT @@ -98,7 +144,8 @@ class FormatSpecTemplate template FormatSpecTemplate(string_view s) // NOLINT __attribute__((enable_if(str_format_internal::EnsureConstexpr(s), - "constexpr trap"))) { + "constexpr trap"))) + : Base("to avoid noise in the compiler error") { static_assert(sizeof(T*) == 0, "Format specified does not match the arguments passed."); } @@ -119,29 +166,19 @@ class FormatSpecTemplate #endif // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER - template ::type> + template FormatSpecTemplate(const ExtendedParsedFormat& pc) // NOLINT - : Base(&pc) {} + : Base(&pc) { + CheckArity(); + CheckMatches(absl::make_index_sequence{}); + } }; class Streamable { public: Streamable(const UntypedFormatSpecImpl& format, absl::Span args) - : format_(format) { - if (args.size() <= ABSL_ARRAYSIZE(few_args_)) { - for (size_t i = 0; i < args.size(); ++i) { - few_args_[i] = args[i]; - } - args_ = absl::MakeSpan(few_args_, args.size()); - } else { - many_args_.assign(args.begin(), args.end()); - args_ = many_args_; - } - } + : format_(format), args_(args.begin(), args.end()) {} std::ostream& Print(std::ostream& os) const; @@ -151,12 +188,7 @@ class Streamable { private: const UntypedFormatSpecImpl& format_; - absl::Span args_; - // if args_.size() is 4 or less: - FormatArgImpl few_args_[4] = {FormatArgImpl(0), FormatArgImpl(0), - FormatArgImpl(0), FormatArgImpl(0)}; - // if args_.size() is more than 4: - std::vector many_args_; + absl::InlinedVector args_; }; // for testing @@ -165,8 +197,7 @@ std::string Summarize(UntypedFormatSpecImpl format, bool BindWithPack(const UnboundConversion* props, absl::Span pack, BoundConversion* bound); -bool FormatUntyped(FormatRawSinkImpl raw_sink, - UntypedFormatSpecImpl format, +bool FormatUntyped(FormatRawSinkImpl raw_sink, UntypedFormatSpecImpl format, absl::Span args); std::string& AppendPack(std::string* out, UntypedFormatSpecImpl format, @@ -185,13 +216,14 @@ int SnprintF(char* output, size_t size, UntypedFormatSpecImpl format, template class StreamedWrapper { public: - explicit StreamedWrapper(const T& v) : v_(v) { } + explicit StreamedWrapper(const T& v) : v_(v) {} private: template - friend ArgConvertResult FormatConvertImpl( - const StreamedWrapper& v, FormatConversionSpecImpl conv, - FormatSinkImpl* out); + friend ArgConvertResult + FormatConvertImpl(const StreamedWrapper& v, FormatConversionSpecImpl conv, + FormatSinkImpl* out); const T& v_; }; diff --git a/Siv3D/include/ThirdParty/absl/strings/internal/str_format/checker.h b/Siv3D/include/ThirdParty/absl/strings/internal/str_format/checker.h index 424c51f74f..eab6ab9d00 100644 --- a/Siv3D/include/ThirdParty/absl/strings/internal/str_format/checker.h +++ b/Siv3D/include/ThirdParty/absl/strings/internal/str_format/checker.h @@ -1,313 +1,94 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_ #define ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_ +#include + #include "absl/base/attributes.h" #include "absl/strings/internal/str_format/arg.h" +#include "absl/strings/internal/str_format/constexpr_parser.h" #include "absl/strings/internal/str_format/extension.h" // Compile time check support for entry points. #ifndef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER -#if ABSL_HAVE_ATTRIBUTE(enable_if) && !defined(__native_client__) +// We disable format checker under vscode intellisense compilation. +// See https://github.com/microsoft/vscode-cpptools/issues/3683 for +// more details. +#if ABSL_HAVE_ATTRIBUTE(enable_if) && !defined(__native_client__) && \ + !defined(__INTELLISENSE__) #define ABSL_INTERNAL_ENABLE_FORMAT_CHECKER 1 -#endif // ABSL_HAVE_ATTRIBUTE(enable_if) && !defined(__native_client__) +#endif // ABSL_HAVE_ATTRIBUTE(enable_if) && !defined(__native_client__) && + // !defined(__INTELLISENSE__) #endif // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER namespace absl { ABSL_NAMESPACE_BEGIN namespace str_format_internal { -constexpr bool AllOf() { return true; } - -template -constexpr bool AllOf(bool b, T... t) { - return b && AllOf(t...); -} - #ifdef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER -constexpr bool ContainsChar(const char* chars, char c) { - return *chars == c || (*chars && ContainsChar(chars + 1, c)); -} - -// A constexpr compatible list of Convs. -struct ConvList { - const FormatConversionCharSet* array; - int count; - - // We do the bound check here to avoid having to do it on the callers. - // Returning an empty FormatConversionCharSet has the same effect as - // short circuiting because it will never match any conversion. - constexpr FormatConversionCharSet operator[](int i) const { - return i < count ? array[i] : FormatConversionCharSet{}; - } - - constexpr ConvList without_front() const { - return count != 0 ? ConvList{array + 1, count - 1} : *this; - } -}; - -template -struct ConvListT { - // Make sure the array has size > 0. - FormatConversionCharSet list[count ? count : 1]; -}; - -constexpr char GetChar(string_view str, size_t index) { - return index < str.size() ? str[index] : char{}; -} - -constexpr string_view ConsumeFront(string_view str, size_t len = 1) { - return len <= str.size() ? string_view(str.data() + len, str.size() - len) - : string_view(); -} - -constexpr string_view ConsumeAnyOf(string_view format, const char* chars) { - return ContainsChar(chars, GetChar(format, 0)) - ? ConsumeAnyOf(ConsumeFront(format), chars) - : format; -} - -constexpr bool IsDigit(char c) { return c >= '0' && c <= '9'; } - -// Helper class for the ParseDigits function. -// It encapsulates the two return values we need there. -struct Integer { - string_view format; - int value; - - // If the next character is a '$', consume it. - // Otherwise, make `this` an invalid positional argument. - constexpr Integer ConsumePositionalDollar() const { - return GetChar(format, 0) == '$' ? Integer{ConsumeFront(format), value} - : Integer{format, 0}; - } -}; - -constexpr Integer ParseDigits(string_view format, int value = 0) { - return IsDigit(GetChar(format, 0)) - ? ParseDigits(ConsumeFront(format), - 10 * value + GetChar(format, 0) - '0') - : Integer{format, value}; -} - -// Parse digits for a positional argument. -// The parsing also consumes the '$'. -constexpr Integer ParsePositional(string_view format) { - return ParseDigits(format).ConsumePositionalDollar(); -} - -// Parses a single conversion specifier. -// See ConvParser::Run() for post conditions. -class ConvParser { - constexpr ConvParser SetFormat(string_view format) const { - return ConvParser(format, args_, error_, arg_position_, is_positional_); - } - - constexpr ConvParser SetArgs(ConvList args) const { - return ConvParser(format_, args, error_, arg_position_, is_positional_); - } - - constexpr ConvParser SetError(bool error) const { - return ConvParser(format_, args_, error_ || error, arg_position_, - is_positional_); - } - - constexpr ConvParser SetArgPosition(int arg_position) const { - return ConvParser(format_, args_, error_, arg_position, is_positional_); - } - - // Consumes the next arg and verifies that it matches `conv`. - // `error_` is set if there is no next arg or if it doesn't match `conv`. - constexpr ConvParser ConsumeNextArg(char conv) const { - return SetArgs(args_.without_front()).SetError(!Contains(args_[0], conv)); - } - - // Verify that positional argument `i.value` matches `conv`. - // `error_` is set if `i.value` is not a valid argument or if it doesn't - // match. - constexpr ConvParser VerifyPositional(Integer i, char conv) const { - return SetFormat(i.format).SetError(!Contains(args_[i.value - 1], conv)); - } - - // Parse the position of the arg and store it in `arg_position_`. - constexpr ConvParser ParseArgPosition(Integer arg) const { - return SetFormat(arg.format).SetArgPosition(arg.value); - } - - // Consume the flags. - constexpr ConvParser ParseFlags() const { - return SetFormat(ConsumeAnyOf(format_, "-+ #0")); - } - - // Consume the width. - // If it is '*', we verify that it matches `args_`. `error_` is set if it - // doesn't match. - constexpr ConvParser ParseWidth() const { - return IsDigit(GetChar(format_, 0)) - ? SetFormat(ParseDigits(format_).format) - : GetChar(format_, 0) == '*' - ? is_positional_ - ? VerifyPositional( - ParsePositional(ConsumeFront(format_)), '*') - : SetFormat(ConsumeFront(format_)) - .ConsumeNextArg('*') - : *this; - } - - // Consume the precision. - // If it is '*', we verify that it matches `args_`. `error_` is set if it - // doesn't match. - constexpr ConvParser ParsePrecision() const { - return GetChar(format_, 0) != '.' - ? *this - : GetChar(format_, 1) == '*' - ? is_positional_ - ? VerifyPositional( - ParsePositional(ConsumeFront(format_, 2)), '*') - : SetFormat(ConsumeFront(format_, 2)) - .ConsumeNextArg('*') - : SetFormat(ParseDigits(ConsumeFront(format_)).format); - } - - // Consume the length characters. - constexpr ConvParser ParseLength() const { - return SetFormat(ConsumeAnyOf(format_, "lLhjztq")); - } - - // Consume the conversion character and verify that it matches `args_`. - // `error_` is set if it doesn't match. - constexpr ConvParser ParseConversion() const { - return is_positional_ - ? VerifyPositional({ConsumeFront(format_), arg_position_}, - GetChar(format_, 0)) - : ConsumeNextArg(GetChar(format_, 0)) - .SetFormat(ConsumeFront(format_)); - } - - constexpr ConvParser(string_view format, ConvList args, bool error, - int arg_position, bool is_positional) - : format_(format), - args_(args), - error_(error), - arg_position_(arg_position), - is_positional_(is_positional) {} - - public: - constexpr ConvParser(string_view format, ConvList args, bool is_positional) - : format_(format), - args_(args), - error_(false), - arg_position_(0), - is_positional_(is_positional) {} - - // Consume the whole conversion specifier. - // `format()` will be set to the character after the conversion character. - // `error()` will be set if any of the arguments do not match. - constexpr ConvParser Run() const { - return (is_positional_ ? ParseArgPosition(ParsePositional(format_)) : *this) - .ParseFlags() - .ParseWidth() - .ParsePrecision() - .ParseLength() - .ParseConversion(); - } - - constexpr string_view format() const { return format_; } - constexpr ConvList args() const { return args_; } - constexpr bool error() const { return error_; } - constexpr bool is_positional() const { return is_positional_; } - - private: - string_view format_; - // Current list of arguments. If we are not in positional mode we will consume - // from the front. - ConvList args_; - bool error_; - // Holds the argument position of the conversion character, if we are in - // positional mode. Otherwise, it is unspecified. - int arg_position_; - // Whether we are in positional mode. - // It changes the behavior of '*' and where to find the converted argument. - bool is_positional_; -}; - -// Parses a whole format expression. -// See FormatParser::Run(). -class FormatParser { - static constexpr bool FoundPercent(string_view format) { - return format.empty() || - (GetChar(format, 0) == '%' && GetChar(format, 1) != '%'); - } - - // We use an inner function to increase the recursion limit. - // The inner function consumes up to `limit` characters on every run. - // This increases the limit from 512 to ~512*limit. - static constexpr string_view ConsumeNonPercentInner(string_view format, - int limit = 20) { - return FoundPercent(format) || !limit - ? format - : ConsumeNonPercentInner( - ConsumeFront(format, GetChar(format, 0) == '%' && - GetChar(format, 1) == '%' - ? 2 - : 1), - limit - 1); - } - - // Consume characters until the next conversion spec %. - // It skips %%. - static constexpr string_view ConsumeNonPercent(string_view format) { - return FoundPercent(format) - ? format - : ConsumeNonPercent(ConsumeNonPercentInner(format)); - } - - static constexpr bool IsPositional(string_view format) { - return IsDigit(GetChar(format, 0)) ? IsPositional(ConsumeFront(format)) - : GetChar(format, 0) == '$'; - } - - constexpr bool RunImpl(bool is_positional) const { - // In non-positional mode we require all arguments to be consumed. - // In positional mode just reaching the end of the format without errors is - // enough. - return (format_.empty() && (is_positional || args_.count == 0)) || - (!format_.empty() && - ValidateArg( - ConvParser(ConsumeFront(format_), args_, is_positional).Run())); - } - - constexpr bool ValidateArg(ConvParser conv) const { - return !conv.error() && FormatParser(conv.format(), conv.args()) - .RunImpl(conv.is_positional()); - } - - public: - constexpr FormatParser(string_view format, ConvList args) - : format_(ConsumeNonPercent(format)), args_(args) {} - - // Runs the parser for `format` and `args`. - // It verifies that the format is valid and that all conversion specifiers - // match the arguments passed. - // In non-positional mode it also verfies that all arguments are consumed. - constexpr bool Run() const { - return RunImpl(!format_.empty() && IsPositional(ConsumeFront(format_))); - } - - private: - string_view format_; - // Current list of arguments. - // If we are not in positional mode we will consume from the front and will - // have to be empty in the end. - ConvList args_; -}; - template constexpr bool ValidFormatImpl(string_view format) { - return FormatParser(format, - {ConvListT{{C...}}.list, sizeof...(C)}) - .Run(); + int next_arg = 0; + const char* p = format.data(); + const char* const end = p + format.size(); + constexpr FormatConversionCharSet + kAllowedConvs[(std::max)(sizeof...(C), size_t{1})] = {C...}; + bool used[(std::max)(sizeof...(C), size_t{1})]{}; + constexpr int kNumArgs = sizeof...(C); + while (p != end) { + while (p != end && *p != '%') ++p; + if (p == end) { + break; + } + if (p + 1 >= end) return false; + if (p[1] == '%') { + // %% + p += 2; + continue; + } + + UnboundConversion conv(absl::kConstInit); + p = ConsumeUnboundConversion(p + 1, end, &conv, &next_arg); + if (p == nullptr) return false; + if (conv.arg_position <= 0 || conv.arg_position > kNumArgs) { + return false; + } + if (!Contains(kAllowedConvs[conv.arg_position - 1], conv.conv)) { + return false; + } + used[conv.arg_position - 1] = true; + for (auto extra : {conv.width, conv.precision}) { + if (extra.is_from_arg()) { + int pos = extra.get_from_arg(); + if (pos <= 0 || pos > kNumArgs) return false; + used[pos - 1] = true; + if (!Contains(kAllowedConvs[pos - 1], '*')) { + return false; + } + } + } + } + if (sizeof...(C) != 0) { + for (bool b : used) { + if (!b) return false; + } + } + return true; } #endif // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER diff --git a/Siv3D/include/ThirdParty/absl/strings/internal/str_format/constexpr_parser.h b/Siv3D/include/ThirdParty/absl/strings/internal/str_format/constexpr_parser.h new file mode 100644 index 0000000000..b70a16e40c --- /dev/null +++ b/Siv3D/include/ThirdParty/absl/strings/internal/str_format/constexpr_parser.h @@ -0,0 +1,352 @@ +// Copyright 2022 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_CONSTEXPR_PARSER_H_ +#define ABSL_STRINGS_INTERNAL_STR_FORMAT_CONSTEXPR_PARSER_H_ + +#include +#include +#include + +#include "absl/base/const_init.h" +#include "absl/strings/internal/str_format/extension.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace str_format_internal { + +enum class LengthMod : std::uint8_t { h, hh, l, ll, L, j, z, t, q, none }; + +// The analyzed properties of a single specified conversion. +struct UnboundConversion { + // This is a user defined default constructor on purpose to skip the + // initialization of parts of the object that are not necessary. + UnboundConversion() {} // NOLINT + + // This constructor is provided for the static checker. We don't want to do + // the unnecessary initialization in the normal case. + explicit constexpr UnboundConversion(absl::ConstInitType) + : arg_position{}, width{}, precision{} {} + + class InputValue { + public: + constexpr void set_value(int value) { + assert(value >= 0); + value_ = value; + } + constexpr int value() const { return value_; } + + // Marks the value as "from arg". aka the '*' format. + // Requires `value >= 1`. + // When set, is_from_arg() return true and get_from_arg() returns the + // original value. + // `value()`'s return value is unspecified in this state. + constexpr void set_from_arg(int value) { + assert(value > 0); + value_ = -value - 1; + } + constexpr bool is_from_arg() const { return value_ < -1; } + constexpr int get_from_arg() const { + assert(is_from_arg()); + return -value_ - 1; + } + + private: + int value_ = -1; + }; + + // No need to initialize. It will always be set in the parser. + int arg_position; + + InputValue width; + InputValue precision; + + Flags flags = Flags::kBasic; + LengthMod length_mod = LengthMod::none; + FormatConversionChar conv = FormatConversionCharInternal::kNone; +}; + +// Helper tag class for the table below. +// It allows fast `char -> ConversionChar/LengthMod/Flags` checking and +// conversions. +class ConvTag { + public: + constexpr ConvTag(FormatConversionChar conversion_char) // NOLINT + : tag_(static_cast(conversion_char)) {} + constexpr ConvTag(LengthMod length_mod) // NOLINT + : tag_(0x80 | static_cast(length_mod)) {} + constexpr ConvTag(Flags flags) // NOLINT + : tag_(0xc0 | static_cast(flags)) {} + constexpr ConvTag() : tag_(0xFF) {} + + constexpr bool is_conv() const { return (tag_ & 0x80) == 0; } + constexpr bool is_length() const { return (tag_ & 0xC0) == 0x80; } + constexpr bool is_flags() const { return (tag_ & 0xE0) == 0xC0; } + + constexpr FormatConversionChar as_conv() const { + assert(is_conv()); + assert(!is_length()); + assert(!is_flags()); + return static_cast(tag_); + } + constexpr LengthMod as_length() const { + assert(!is_conv()); + assert(is_length()); + assert(!is_flags()); + return static_cast(tag_ & 0x3F); + } + constexpr Flags as_flags() const { + assert(!is_conv()); + assert(!is_length()); + assert(is_flags()); + return static_cast(tag_ & 0x1F); + } + + private: + uint8_t tag_; +}; + +struct ConvTagHolder { + using CC = FormatConversionCharInternal; + using LM = LengthMod; + + // Abbreviations to fit in the table below. + static constexpr auto kFSign = Flags::kSignCol; + static constexpr auto kFAlt = Flags::kAlt; + static constexpr auto kFPos = Flags::kShowPos; + static constexpr auto kFLeft = Flags::kLeft; + static constexpr auto kFZero = Flags::kZero; + + static constexpr ConvTag value[256] = { + {}, {}, {}, {}, {}, {}, {}, {}, // 00-07 + {}, {}, {}, {}, {}, {}, {}, {}, // 08-0f + {}, {}, {}, {}, {}, {}, {}, {}, // 10-17 + {}, {}, {}, {}, {}, {}, {}, {}, // 18-1f + kFSign, {}, {}, kFAlt, {}, {}, {}, {}, // !"#$%&' + {}, {}, {}, kFPos, {}, kFLeft, {}, {}, // ()*+,-./ + kFZero, {}, {}, {}, {}, {}, {}, {}, // 01234567 + {}, {}, {}, {}, {}, {}, {}, {}, // 89:;<=>? + {}, CC::A, {}, {}, {}, CC::E, CC::F, CC::G, // @ABCDEFG + {}, {}, {}, {}, LM::L, {}, {}, {}, // HIJKLMNO + {}, {}, {}, {}, {}, {}, {}, {}, // PQRSTUVW + CC::X, {}, {}, {}, {}, {}, {}, {}, // XYZ[\]^_ + {}, CC::a, {}, CC::c, CC::d, CC::e, CC::f, CC::g, // `abcdefg + LM::h, CC::i, LM::j, {}, LM::l, {}, CC::n, CC::o, // hijklmno + CC::p, LM::q, {}, CC::s, LM::t, CC::u, CC::v, {}, // pqrstuvw + CC::x, {}, LM::z, {}, {}, {}, {}, {}, // xyz{|}! + {}, {}, {}, {}, {}, {}, {}, {}, // 80-87 + {}, {}, {}, {}, {}, {}, {}, {}, // 88-8f + {}, {}, {}, {}, {}, {}, {}, {}, // 90-97 + {}, {}, {}, {}, {}, {}, {}, {}, // 98-9f + {}, {}, {}, {}, {}, {}, {}, {}, // a0-a7 + {}, {}, {}, {}, {}, {}, {}, {}, // a8-af + {}, {}, {}, {}, {}, {}, {}, {}, // b0-b7 + {}, {}, {}, {}, {}, {}, {}, {}, // b8-bf + {}, {}, {}, {}, {}, {}, {}, {}, // c0-c7 + {}, {}, {}, {}, {}, {}, {}, {}, // c8-cf + {}, {}, {}, {}, {}, {}, {}, {}, // d0-d7 + {}, {}, {}, {}, {}, {}, {}, {}, // d8-df + {}, {}, {}, {}, {}, {}, {}, {}, // e0-e7 + {}, {}, {}, {}, {}, {}, {}, {}, // e8-ef + {}, {}, {}, {}, {}, {}, {}, {}, // f0-f7 + {}, {}, {}, {}, {}, {}, {}, {}, // f8-ff + }; +}; + +// Keep a single table for all the conversion chars and length modifiers. +constexpr ConvTag GetTagForChar(char c) { + return ConvTagHolder::value[static_cast(c)]; +} + +constexpr bool CheckFastPathSetting(const UnboundConversion& conv) { + bool width_precision_needed = + conv.width.value() >= 0 || conv.precision.value() >= 0; + if (width_precision_needed && conv.flags == Flags::kBasic) { +#if defined(__clang__) + // Some compilers complain about this in constexpr even when not executed, + // so only enable the error dump in clang. + fprintf(stderr, + "basic=%d left=%d show_pos=%d sign_col=%d alt=%d zero=%d " + "width=%d precision=%d\n", + conv.flags == Flags::kBasic ? 1 : 0, + FlagsContains(conv.flags, Flags::kLeft) ? 1 : 0, + FlagsContains(conv.flags, Flags::kShowPos) ? 1 : 0, + FlagsContains(conv.flags, Flags::kSignCol) ? 1 : 0, + FlagsContains(conv.flags, Flags::kAlt) ? 1 : 0, + FlagsContains(conv.flags, Flags::kZero) ? 1 : 0, conv.width.value(), + conv.precision.value()); +#endif // defined(__clang__) + return false; + } + return true; +} + +constexpr int ParseDigits(char& c, const char*& pos, const char* const end) { + int digits = c - '0'; + // We do not want to overflow `digits` so we consume at most digits10 + // digits. If there are more digits the parsing will fail later on when the + // digit doesn't match the expected characters. + int num_digits = std::numeric_limits::digits10; + for (;;) { + if (ABSL_PREDICT_FALSE(pos == end)) break; + c = *pos++; + if ('0' > c || c > '9') break; + --num_digits; + if (ABSL_PREDICT_FALSE(!num_digits)) break; + digits = 10 * digits + c - '0'; + } + return digits; +} + +template +constexpr const char* ConsumeConversion(const char* pos, const char* const end, + UnboundConversion* conv, + int* next_arg) { + const char* const original_pos = pos; + char c = 0; + // Read the next char into `c` and update `pos`. Returns false if there are + // no more chars to read. +#define ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR() \ + do { \ + if (ABSL_PREDICT_FALSE(pos == end)) return nullptr; \ + c = *pos++; \ + } while (0) + + if (is_positional) { + ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); + if (ABSL_PREDICT_FALSE(c < '1' || c > '9')) return nullptr; + conv->arg_position = ParseDigits(c, pos, end); + assert(conv->arg_position > 0); + if (ABSL_PREDICT_FALSE(c != '$')) return nullptr; + } + + ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); + + // We should start with the basic flag on. + assert(conv->flags == Flags::kBasic); + + // Any non alpha character makes this conversion not basic. + // This includes flags (-+ #0), width (1-9, *) or precision (.). + // All conversion characters and length modifiers are alpha characters. + if (c < 'A') { + while (c <= '0') { + auto tag = GetTagForChar(c); + if (tag.is_flags()) { + conv->flags = conv->flags | tag.as_flags(); + ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); + } else { + break; + } + } + + if (c <= '9') { + if (c >= '0') { + int maybe_width = ParseDigits(c, pos, end); + if (!is_positional && c == '$') { + if (ABSL_PREDICT_FALSE(*next_arg != 0)) return nullptr; + // Positional conversion. + *next_arg = -1; + return ConsumeConversion(original_pos, end, conv, next_arg); + } + conv->flags = conv->flags | Flags::kNonBasic; + conv->width.set_value(maybe_width); + } else if (c == '*') { + conv->flags = conv->flags | Flags::kNonBasic; + ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); + if (is_positional) { + if (ABSL_PREDICT_FALSE(c < '1' || c > '9')) return nullptr; + conv->width.set_from_arg(ParseDigits(c, pos, end)); + if (ABSL_PREDICT_FALSE(c != '$')) return nullptr; + ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); + } else { + conv->width.set_from_arg(++*next_arg); + } + } + } + + if (c == '.') { + conv->flags = conv->flags | Flags::kNonBasic; + ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); + if ('0' <= c && c <= '9') { + conv->precision.set_value(ParseDigits(c, pos, end)); + } else if (c == '*') { + ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); + if (is_positional) { + if (ABSL_PREDICT_FALSE(c < '1' || c > '9')) return nullptr; + conv->precision.set_from_arg(ParseDigits(c, pos, end)); + if (c != '$') return nullptr; + ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); + } else { + conv->precision.set_from_arg(++*next_arg); + } + } else { + conv->precision.set_value(0); + } + } + } + + auto tag = GetTagForChar(c); + + if (ABSL_PREDICT_FALSE(c == 'v' && conv->flags != Flags::kBasic)) { + return nullptr; + } + + if (ABSL_PREDICT_FALSE(!tag.is_conv())) { + if (ABSL_PREDICT_FALSE(!tag.is_length())) return nullptr; + + // It is a length modifier. + using str_format_internal::LengthMod; + LengthMod length_mod = tag.as_length(); + ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); + if (c == 'h' && length_mod == LengthMod::h) { + conv->length_mod = LengthMod::hh; + ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); + } else if (c == 'l' && length_mod == LengthMod::l) { + conv->length_mod = LengthMod::ll; + ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); + } else { + conv->length_mod = length_mod; + } + tag = GetTagForChar(c); + + if (ABSL_PREDICT_FALSE(c == 'v')) return nullptr; + if (ABSL_PREDICT_FALSE(!tag.is_conv())) return nullptr; + } +#undef ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR + + assert(CheckFastPathSetting(*conv)); + (void)(&CheckFastPathSetting); + + conv->conv = tag.as_conv(); + if (!is_positional) conv->arg_position = ++*next_arg; + return pos; +} + +// Consume conversion spec prefix (not including '%') of [p, end) if valid. +// Examples of valid specs would be e.g.: "s", "d", "-12.6f". +// If valid, it returns the first character following the conversion spec, +// and the spec part is broken down and returned in 'conv'. +// If invalid, returns nullptr. +constexpr const char* ConsumeUnboundConversion(const char* p, const char* end, + UnboundConversion* conv, + int* next_arg) { + if (*next_arg < 0) return ConsumeConversion(p, end, conv, next_arg); + return ConsumeConversion(p, end, conv, next_arg); +} + +} // namespace str_format_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_CONSTEXPR_PARSER_H_ diff --git a/Siv3D/include/ThirdParty/absl/strings/internal/str_format/extension.h b/Siv3D/include/ThirdParty/absl/strings/internal/str_format/extension.h index a9b9e137de..8de42d2cfc 100644 --- a/Siv3D/include/ThirdParty/absl/strings/internal/str_format/extension.h +++ b/Siv3D/include/ThirdParty/absl/strings/internal/str_format/extension.h @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -70,7 +71,7 @@ class FormatSinkImpl { ~FormatSinkImpl() { Flush(); } void Flush() { - raw_.Write(string_view(buf_, pos_ - buf_)); + raw_.Write(string_view(buf_, static_cast(pos_ - buf_))); pos_ = buf_; } @@ -120,7 +121,9 @@ class FormatSinkImpl { } private: - size_t Avail() const { return buf_ + sizeof(buf_) - pos_; } + size_t Avail() const { + return static_cast(buf_ + sizeof(buf_) - pos_); + } FormatRawSinkImpl raw_; size_t size_ = 0; @@ -128,19 +131,33 @@ class FormatSinkImpl { char buf_[1024]; }; -struct Flags { - bool basic : 1; // fastest conversion: no flags, width, or precision - bool left : 1; // "-" - bool show_pos : 1; // "+" - bool sign_col : 1; // " " - bool alt : 1; // "#" - bool zero : 1; // "0" - std::string ToString() const; - friend std::ostream& operator<<(std::ostream& os, const Flags& v) { - return os << v.ToString(); - } +enum class Flags : uint8_t { + kBasic = 0, + kLeft = 1 << 0, + kShowPos = 1 << 1, + kSignCol = 1 << 2, + kAlt = 1 << 3, + kZero = 1 << 4, + // This is not a real flag. It just exists to turn off kBasic when no other + // flags are set. This is for when width/precision are specified. + kNonBasic = 1 << 5, }; +constexpr Flags operator|(Flags a, Flags b) { + return static_cast(static_cast(a) | static_cast(b)); +} + +constexpr bool FlagsContains(Flags haystack, Flags needle) { + return (static_cast(haystack) & static_cast(needle)) == + static_cast(needle); +} + +std::string FlagsToString(Flags v); + +inline std::ostream& operator<<(std::ostream& os, Flags v) { + return os << FlagsToString(v); +} + // clang-format off #define ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(X_VAL, X_SEP) \ /* text */ \ @@ -152,7 +169,7 @@ struct Flags { X_VAL(f) X_SEP X_VAL(F) X_SEP X_VAL(e) X_SEP X_VAL(E) X_SEP \ X_VAL(g) X_SEP X_VAL(G) X_SEP X_VAL(a) X_SEP X_VAL(A) X_SEP \ /* misc */ \ - X_VAL(n) X_SEP X_VAL(p) + X_VAL(n) X_SEP X_VAL(p) X_SEP X_VAL(v) // clang-format on // This type should not be referenced, it exists only to provide labels @@ -174,7 +191,7 @@ struct FormatConversionCharInternal { c, s, // text d, i, o, u, x, X, // int f, F, e, E, g, G, a, A, // float - n, p, // misc + n, p, v, // misc kNone }; // clang-format on @@ -256,13 +273,17 @@ struct FormatConversionSpecImplFriend; class FormatConversionSpecImpl { public: - // Width and precison are not specified, no flags are set. - bool is_basic() const { return flags_.basic; } - bool has_left_flag() const { return flags_.left; } - bool has_show_pos_flag() const { return flags_.show_pos; } - bool has_sign_col_flag() const { return flags_.sign_col; } - bool has_alt_flag() const { return flags_.alt; } - bool has_zero_flag() const { return flags_.zero; } + // Width and precision are not specified, no flags are set. + bool is_basic() const { return flags_ == Flags::kBasic; } + bool has_left_flag() const { return FlagsContains(flags_, Flags::kLeft); } + bool has_show_pos_flag() const { + return FlagsContains(flags_, Flags::kShowPos); + } + bool has_sign_col_flag() const { + return FlagsContains(flags_, Flags::kSignCol); + } + bool has_alt_flag() const { return FlagsContains(flags_, Flags::kAlt); } + bool has_zero_flag() const { return FlagsContains(flags_, Flags::kZero); } FormatConversionChar conversion_char() const { // Keep this field first in the struct . It generates better code when @@ -271,6 +292,8 @@ class FormatConversionSpecImpl { return conv_; } + void set_conversion_char(FormatConversionChar c) { conv_ = c; } + // Returns the specified width. If width is unspecfied, it returns a negative // value. int width() const { return width_; } @@ -306,7 +329,7 @@ struct FormatConversionSpecImplFriend final { conv->precision_ = p; } static std::string FlagsToString(const FormatConversionSpecImpl& spec) { - return spec.flags_.ToString(); + return str_format_internal::FlagsToString(spec.flags_); } }; diff --git a/Siv3D/include/ThirdParty/absl/strings/internal/str_format/float_conversion.h b/Siv3D/include/ThirdParty/absl/strings/internal/str_format/float_conversion.h index e78bc19106..71100e7142 100644 --- a/Siv3D/include/ThirdParty/absl/strings/internal/str_format/float_conversion.h +++ b/Siv3D/include/ThirdParty/absl/strings/internal/str_format/float_conversion.h @@ -1,3 +1,17 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_FLOAT_CONVERSION_H_ #define ABSL_STRINGS_INTERNAL_STR_FORMAT_FLOAT_CONVERSION_H_ diff --git a/Siv3D/include/ThirdParty/absl/strings/internal/str_format/output.h b/Siv3D/include/ThirdParty/absl/strings/internal/str_format/output.h index 8030dae00f..15e751ab6f 100644 --- a/Siv3D/include/ThirdParty/absl/strings/internal/str_format/output.h +++ b/Siv3D/include/ThirdParty/absl/strings/internal/str_format/output.h @@ -22,6 +22,7 @@ #define ABSL_STRINGS_INTERNAL_STR_FORMAT_OUTPUT_H_ #include +#include #include #include @@ -71,7 +72,7 @@ inline void AbslFormatFlush(std::string* out, string_view s) { out->append(s.data(), s.size()); } inline void AbslFormatFlush(std::ostream* out, string_view s) { - out->write(s.data(), s.size()); + out->write(s.data(), static_cast(s.size())); } inline void AbslFormatFlush(FILERawSink* sink, string_view v) { diff --git a/Siv3D/include/ThirdParty/absl/strings/internal/str_format/parser.h b/Siv3D/include/ThirdParty/absl/strings/internal/str_format/parser.h index fffed04fa0..35b6d49c14 100644 --- a/Siv3D/include/ThirdParty/absl/strings/internal/str_format/parser.h +++ b/Siv3D/include/ThirdParty/absl/strings/internal/str_format/parser.h @@ -1,3 +1,17 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_PARSER_H_ #define ABSL_STRINGS_INTERNAL_STR_FORMAT_PARSER_H_ @@ -15,103 +29,18 @@ #include #include "absl/strings/internal/str_format/checker.h" +#include "absl/strings/internal/str_format/constexpr_parser.h" #include "absl/strings/internal/str_format/extension.h" namespace absl { ABSL_NAMESPACE_BEGIN namespace str_format_internal { -enum class LengthMod : std::uint8_t { h, hh, l, ll, L, j, z, t, q, none }; - std::string LengthModToString(LengthMod v); -// The analyzed properties of a single specified conversion. -struct UnboundConversion { - UnboundConversion() - : flags() /* This is required to zero all the fields of flags. */ { - flags.basic = true; - } - - class InputValue { - public: - void set_value(int value) { - assert(value >= 0); - value_ = value; - } - int value() const { return value_; } - - // Marks the value as "from arg". aka the '*' format. - // Requires `value >= 1`. - // When set, is_from_arg() return true and get_from_arg() returns the - // original value. - // `value()`'s return value is unspecfied in this state. - void set_from_arg(int value) { - assert(value > 0); - value_ = -value - 1; - } - bool is_from_arg() const { return value_ < -1; } - int get_from_arg() const { - assert(is_from_arg()); - return -value_ - 1; - } - - private: - int value_ = -1; - }; - - // No need to initialize. It will always be set in the parser. - int arg_position; - - InputValue width; - InputValue precision; - - Flags flags; - LengthMod length_mod = LengthMod::none; - FormatConversionChar conv = FormatConversionCharInternal::kNone; -}; - -// Consume conversion spec prefix (not including '%') of [p, end) if valid. -// Examples of valid specs would be e.g.: "s", "d", "-12.6f". -// If valid, it returns the first character following the conversion spec, -// and the spec part is broken down and returned in 'conv'. -// If invalid, returns nullptr. -const char* ConsumeUnboundConversion(const char* p, const char* end, - UnboundConversion* conv, int* next_arg); - -// Helper tag class for the table below. -// It allows fast `char -> ConversionChar/LengthMod` checking and -// conversions. -class ConvTag { - public: - constexpr ConvTag(FormatConversionChar conversion_char) // NOLINT - : tag_(static_cast(conversion_char)) {} - // We invert the length modifiers to make them negative so that we can easily - // test for them. - constexpr ConvTag(LengthMod length_mod) // NOLINT - : tag_(~static_cast(length_mod)) {} - // Everything else is -128, which is negative to make is_conv() simpler. - constexpr ConvTag() : tag_(-128) {} - - bool is_conv() const { return tag_ >= 0; } - bool is_length() const { return tag_ < 0 && tag_ != -128; } - FormatConversionChar as_conv() const { - assert(is_conv()); - return static_cast(tag_); - } - LengthMod as_length() const { - assert(is_length()); - return static_cast(~tag_); - } - - private: - std::int8_t tag_; -}; - -extern const ConvTag kTags[256]; -// Keep a single table for all the conversion chars and length modifiers. -inline ConvTag GetTagForChar(char c) { - return kTags[static_cast(c)]; -} +const char* ConsumeUnboundConversionNoInline(const char* p, const char* end, + UnboundConversion* conv, + int* next_arg); // Parse the format string provided in 'src' and pass the identified items into // 'consumer'. @@ -129,13 +58,15 @@ bool ParseFormatString(string_view src, Consumer consumer) { const char* p = src.data(); const char* const end = p + src.size(); while (p != end) { - const char* percent = static_cast(memchr(p, '%', end - p)); + const char* percent = + static_cast(memchr(p, '%', static_cast(end - p))); if (!percent) { // We found the last substring. - return consumer.Append(string_view(p, end - p)); + return consumer.Append(string_view(p, static_cast(end - p))); } // We found a percent, so push the text run then process the percent. - if (ABSL_PREDICT_FALSE(!consumer.Append(string_view(p, percent - p)))) { + if (ABSL_PREDICT_FALSE(!consumer.Append( + string_view(p, static_cast(percent - p))))) { return false; } if (ABSL_PREDICT_FALSE(percent + 1 >= end)) return false; @@ -163,10 +94,11 @@ bool ParseFormatString(string_view src, Consumer consumer) { } } else if (percent[1] != '%') { UnboundConversion conv; - p = ConsumeUnboundConversion(percent + 1, end, &conv, &next_arg); + p = ConsumeUnboundConversionNoInline(percent + 1, end, &conv, &next_arg); if (ABSL_PREDICT_FALSE(p == nullptr)) return false; if (ABSL_PREDICT_FALSE(!consumer.ConvertOne( - conv, string_view(percent + 1, p - (percent + 1))))) { + conv, string_view(percent + 1, + static_cast(p - (percent + 1)))))) { return false; } } else { @@ -220,7 +152,8 @@ class ParsedFormatBase { string_view text(base, 0); for (const auto& item : items_) { const char* const end = text.data() + text.size(); - text = string_view(end, (base + item.text_end) - end); + text = + string_view(end, static_cast((base + item.text_end) - end)); if (item.is_conversion) { if (!consumer.ConvertOne(item.conv, text)) return false; } else { diff --git a/Siv3D/include/ThirdParty/absl/strings/internal/str_join_internal.h b/Siv3D/include/ThirdParty/absl/strings/internal/str_join_internal.h index 31dbf672f0..d97d5033d8 100644 --- a/Siv3D/include/ThirdParty/absl/strings/internal/str_join_internal.h +++ b/Siv3D/include/ThirdParty/absl/strings/internal/str_join_internal.h @@ -229,10 +229,11 @@ std::string JoinAlgorithm(Iterator start, Iterator end, absl::string_view s, std::string result; if (start != end) { // Sums size - size_t result_size = start->size(); + auto&& start_value = *start; + size_t result_size = start_value.size(); for (Iterator it = start; ++it != end;) { result_size += s.size(); - result_size += it->size(); + result_size += (*it).size(); } if (result_size > 0) { @@ -240,13 +241,15 @@ std::string JoinAlgorithm(Iterator start, Iterator end, absl::string_view s, // Joins strings char* result_buf = &*result.begin(); - memcpy(result_buf, start->data(), start->size()); - result_buf += start->size(); + + memcpy(result_buf, start_value.data(), start_value.size()); + result_buf += start_value.size(); for (Iterator it = start; ++it != end;) { memcpy(result_buf, s.data(), s.size()); result_buf += s.size(); - memcpy(result_buf, it->data(), it->size()); - result_buf += it->size(); + auto&& value = *it; + memcpy(result_buf, value.data(), value.size()); + result_buf += value.size(); } } } diff --git a/Siv3D/include/ThirdParty/absl/strings/internal/str_split_internal.h b/Siv3D/include/ThirdParty/absl/strings/internal/str_split_internal.h index 6f5bc095fb..081ad85ad4 100644 --- a/Siv3D/include/ThirdParty/absl/strings/internal/str_split_internal.h +++ b/Siv3D/include/ThirdParty/absl/strings/internal/str_split_internal.h @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include #include @@ -51,9 +51,9 @@ ABSL_NAMESPACE_BEGIN namespace strings_internal { // This class is implicitly constructible from everything that absl::string_view -// is implicitly constructible from. If it's constructed from a temporary -// string, the data is moved into a data member so its lifetime matches that of -// the ConvertibleToStringView instance. +// is implicitly constructible from, except for rvalue strings. This means it +// can be used as a function parameter in places where passing a temporary +// string might cause memory lifetime issues. class ConvertibleToStringView { public: ConvertibleToStringView(const char* s) // NOLINT(runtime/explicit) @@ -64,42 +64,13 @@ class ConvertibleToStringView { ConvertibleToStringView(const std::string& s) // NOLINT(runtime/explicit) : value_(s) {} - // Matches rvalue strings and moves their data to a member. - ConvertibleToStringView(std::string&& s) // NOLINT(runtime/explicit) - : copy_(std::move(s)), value_(copy_) {} - - ConvertibleToStringView(const ConvertibleToStringView& other) - : copy_(other.copy_), - value_(other.IsSelfReferential() ? copy_ : other.value_) {} - - ConvertibleToStringView(ConvertibleToStringView&& other) { - StealMembers(std::move(other)); - } - - ConvertibleToStringView& operator=(ConvertibleToStringView other) { - StealMembers(std::move(other)); - return *this; - } + // Disable conversion from rvalue strings. + ConvertibleToStringView(std::string&& s) = delete; + ConvertibleToStringView(const std::string&& s) = delete; absl::string_view value() const { return value_; } private: - // Returns true if ctsp's value refers to its internal copy_ member. - bool IsSelfReferential() const { return value_.data() == copy_.data(); } - - void StealMembers(ConvertibleToStringView&& other) { - if (other.IsSelfReferential()) { - copy_ = std::move(other.copy_); - value_ = copy_; - other.value_ = other.copy_; - } else { - value_ = other.value_; - } - } - - // Holds the data moved from temporary std::string arguments. Declared first - // so that 'value' can refer to 'copy_'. - std::string copy_; absl::string_view value_; }; @@ -161,7 +132,8 @@ class SplitIterator { const absl::string_view text = splitter_->text(); const absl::string_view d = delimiter_.Find(text, pos_); if (d.data() == text.data() + text.size()) state_ = kLastState; - curr_ = text.substr(pos_, d.data() - (text.data() + pos_)); + curr_ = text.substr(pos_, + static_cast(d.data() - (text.data() + pos_))); pos_ += curr_.size() + d.size(); } while (!predicate_(curr_)); return *this; @@ -211,6 +183,13 @@ template struct HasConstIterator> : std::true_type {}; +// HasEmplace::value is true iff there exists a method T::emplace(). +template +struct HasEmplace : std::false_type {}; +template +struct HasEmplace().emplace())>> + : std::true_type {}; + // IsInitializerList::value is true iff T is an std::initializer_list. More // details below in Splitter<> where this is used. std::false_type IsInitializerListDispatch(...); // default: No @@ -256,6 +235,24 @@ struct SplitterIsConvertibleTo HasMappedType::value> { }; +template +struct ShouldUseLifetimeBound : std::false_type {}; + +template +struct ShouldUseLifetimeBound< + StringType, Container, + std::enable_if_t< + std::is_same::value && + std::is_same::value>> + : std::true_type {}; + +template +using ShouldUseLifetimeBoundForPair = std::integral_constant< + bool, std::is_same::value && + (std::is_same::value || + std::is_same::value)>; + + // This class implements the range that is returned by absl::StrSplit(). This // class has templated conversion operators that allow it to be implicitly // converted to a variety of types that the caller may have specified on the @@ -273,7 +270,11 @@ struct SplitterIsConvertibleTo // the split strings: only strings for which the predicate returns true will be // kept. A Predicate object is any unary functor that takes an absl::string_view // and returns bool. -template +// +// The StringType parameter can be either string_view or string, depending on +// whether the Splitter refers to a string stored elsewhere, or if the string +// resides inside the Splitter itself. +template class Splitter { public: using DelimiterType = Delimiter; @@ -281,12 +282,12 @@ class Splitter { using const_iterator = strings_internal::SplitIterator; using value_type = typename std::iterator_traits::value_type; - Splitter(ConvertibleToStringView input_text, Delimiter d, Predicate p) + Splitter(StringType input_text, Delimiter d, Predicate p) : text_(std::move(input_text)), delimiter_(std::move(d)), predicate_(std::move(p)) {} - absl::string_view text() const { return text_.value(); } + absl::string_view text() const { return text_; } const Delimiter& delimiter() const { return delimiter_; } const Predicate& predicate() const { return predicate_; } @@ -298,10 +299,24 @@ class Splitter { // An implicit conversion operator that is restricted to only those containers // that the splitter is convertible to. - template ::value>::type> - operator Container() const { // NOLINT(runtime/explicit) + template < + typename Container, + std::enable_if_t::value && + SplitterIsConvertibleTo::value, + std::nullptr_t> = nullptr> + // NOLINTNEXTLINE(google-explicit-constructor) + operator Container() const ABSL_ATTRIBUTE_LIFETIME_BOUND { + return ConvertToContainer::value>()(*this); + } + + template < + typename Container, + std::enable_if_t::value && + SplitterIsConvertibleTo::value, + std::nullptr_t> = nullptr> + // NOLINTNEXTLINE(google-explicit-constructor) + operator Container() const { return ConvertToContainer::value>()(*this); } @@ -310,8 +325,27 @@ class Splitter { // strings returned by the begin() iterator. Either/both of .first and .second // will be constructed with empty strings if the iterator doesn't have a // corresponding value. + template ::value, + std::nullptr_t> = nullptr> + // NOLINTNEXTLINE(google-explicit-constructor) + operator std::pair() const ABSL_ATTRIBUTE_LIFETIME_BOUND { + return ConvertToPair(); + } + + template ::value, + std::nullptr_t> = nullptr> + // NOLINTNEXTLINE(google-explicit-constructor) + operator std::pair() const { + return ConvertToPair(); + } + + private: template - operator std::pair() const { // NOLINT(runtime/explicit) + std::pair ConvertToPair() const { absl::string_view first, second; auto it = begin(); if (it != end()) { @@ -323,7 +357,6 @@ class Splitter { return {First(first), Second(second)}; } - private: // ConvertToContainer is a functor converting a Splitter to the requested // Container of ValueType. It is specialized below to optimize splitting to // certain combinations of Container and ValueType. @@ -336,7 +369,7 @@ class Splitter { Container operator()(const Splitter& splitter) const { Container c; auto it = std::inserter(c, c.end()); - for (const auto sp : splitter) { + for (const auto& sp : splitter) { *it++ = ValueType(sp); } return c; @@ -397,53 +430,46 @@ class Splitter { // value. template struct ConvertToContainer, true> { + using iterator = typename Container::iterator; + Container operator()(const Splitter& splitter) const { Container m; - typename Container::iterator it; + iterator it; bool insert = true; - for (const auto sp : splitter) { + for (const absl::string_view sv : splitter) { if (insert) { - it = Inserter::Insert(&m, First(sp), Second()); + it = InsertOrEmplace(&m, sv); } else { - it->second = Second(sp); + it->second = Second(sv); } insert = !insert; } return m; } - // Inserts the key and value into the given map, returning an iterator to - // the inserted item. Specialized for std::map and std::multimap to use - // emplace() and adapt emplace()'s return value. - template - struct Inserter { - using M = Map; - template - static typename M::iterator Insert(M* m, Args&&... args) { - return m->insert(std::make_pair(std::forward(args)...)).first; - } - }; - - template - struct Inserter> { - using M = std::map; - template - static typename M::iterator Insert(M* m, Args&&... args) { - return m->emplace(std::make_pair(std::forward(args)...)).first; - } - }; - - template - struct Inserter> { - using M = std::multimap; - template - static typename M::iterator Insert(M* m, Args&&... args) { - return m->emplace(std::make_pair(std::forward(args)...)); - } - }; + // Inserts the key and an empty value into the map, returning an iterator to + // the inserted item. We use emplace() if available, otherwise insert(). + template + static absl::enable_if_t::value, iterator> InsertOrEmplace( + M* m, absl::string_view key) { + // Use piecewise_construct to support old versions of gcc in which pair + // constructor can't otherwise construct string from string_view. + return ToIter(m->emplace(std::piecewise_construct, std::make_tuple(key), + std::tuple<>())); + } + template + static absl::enable_if_t::value, iterator> InsertOrEmplace( + M* m, absl::string_view key) { + return ToIter(m->insert(std::make_pair(First(key), Second("")))); + } + + static iterator ToIter(std::pair pair) { + return pair.first; + } + static iterator ToIter(iterator iter) { return iter; } }; - ConvertibleToStringView text_; + StringType text_; Delimiter delimiter_; Predicate predicate_; }; diff --git a/Siv3D/include/ThirdParty/absl/strings/internal/string_constant.h b/Siv3D/include/ThirdParty/absl/strings/internal/string_constant.h new file mode 100644 index 0000000000..f68b17d75e --- /dev/null +++ b/Siv3D/include/ThirdParty/absl/strings/internal/string_constant.h @@ -0,0 +1,72 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_INTERNAL_STRING_CONSTANT_H_ +#define ABSL_STRINGS_INTERNAL_STRING_CONSTANT_H_ + +#include "absl/meta/type_traits.h" +#include "absl/strings/string_view.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace strings_internal { + +// StringConstant represents a compile time string constant. +// It can be accessed via its `absl::string_view value` static member. +// It is guaranteed that the `string_view` returned has constant `.data()`, +// constant `.size()` and constant `value[i]` for all `0 <= i < .size()` +// +// The `T` is an opaque type. It is guaranteed that different string constants +// will have different values of `T`. This allows users to associate the string +// constant with other static state at compile time. +// +// Instances should be made using the `MakeStringConstant()` factory function +// below. +template +struct StringConstant { + private: + static constexpr bool TryConstexprEval(absl::string_view view) { + return view.empty() || 2 * view[0] != 1; + } + + public: + static constexpr absl::string_view value = T{}(); + constexpr absl::string_view operator()() const { return value; } + + // Check to be sure `view` points to constant data. + // Otherwise, it can't be constant evaluated. + static_assert(TryConstexprEval(value), + "The input string_view must point to constant data."); +}; + +#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL +template +constexpr absl::string_view StringConstant::value; +#endif + +// Factory function for `StringConstant` instances. +// It supports callables that have a constexpr default constructor and a +// constexpr operator(). +// It must return an `absl::string_view` or `const char*` pointing to constant +// data. This is validated at compile time. +template +constexpr StringConstant MakeStringConstant(T) { + return {}; +} + +} // namespace strings_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_STRING_CONSTANT_H_ diff --git a/Siv3D/include/ThirdParty/absl/strings/internal/stringify_sink.h b/Siv3D/include/ThirdParty/absl/strings/internal/stringify_sink.h new file mode 100644 index 0000000000..fc3747bb72 --- /dev/null +++ b/Siv3D/include/ThirdParty/absl/strings/internal/stringify_sink.h @@ -0,0 +1,57 @@ +// Copyright 2022 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_INTERNAL_STRINGIFY_SINK_H_ +#define ABSL_STRINGS_INTERNAL_STRINGIFY_SINK_H_ + +#include +#include +#include + +#include "absl/strings/string_view.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +namespace strings_internal { +class StringifySink { + public: + void Append(size_t count, char ch); + + void Append(string_view v); + + // Support `absl::Format(&sink, format, args...)`. + friend void AbslFormatFlush(StringifySink* sink, absl::string_view v) { + sink->Append(v); + } + + private: + template + friend string_view ExtractStringification(StringifySink& sink, const T& v); + + std::string buffer_; +}; + +template +string_view ExtractStringification(StringifySink& sink, const T& v) { + AbslStringify(sink, v); + return sink.buffer_; +} + +} // namespace strings_internal + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_STRINGIFY_SINK_H_ diff --git a/Siv3D/include/ThirdParty/absl/strings/match.h b/Siv3D/include/ThirdParty/absl/strings/match.h index 90fca98ad2..1eeafbbf67 100644 --- a/Siv3D/include/ThirdParty/absl/strings/match.h +++ b/Siv3D/include/ThirdParty/absl/strings/match.h @@ -43,14 +43,20 @@ ABSL_NAMESPACE_BEGIN // StrContains() // // Returns whether a given string `haystack` contains the substring `needle`. -inline bool StrContains(absl::string_view haystack, absl::string_view needle) { +inline bool StrContains(absl::string_view haystack, + absl::string_view needle) noexcept { return haystack.find(needle, 0) != haystack.npos; } +inline bool StrContains(absl::string_view haystack, char needle) noexcept { + return haystack.find(needle) != haystack.npos; +} + // StartsWith() // // Returns whether a given string `text` begins with `prefix`. -inline bool StartsWith(absl::string_view text, absl::string_view prefix) { +inline bool StartsWith(absl::string_view text, + absl::string_view prefix) noexcept { return prefix.empty() || (text.size() >= prefix.size() && memcmp(text.data(), prefix.data(), prefix.size()) == 0); @@ -59,30 +65,53 @@ inline bool StartsWith(absl::string_view text, absl::string_view prefix) { // EndsWith() // // Returns whether a given string `text` ends with `suffix`. -inline bool EndsWith(absl::string_view text, absl::string_view suffix) { +inline bool EndsWith(absl::string_view text, + absl::string_view suffix) noexcept { return suffix.empty() || (text.size() >= suffix.size() && memcmp(text.data() + (text.size() - suffix.size()), suffix.data(), suffix.size()) == 0); } +// StrContainsIgnoreCase() +// +// Returns whether a given ASCII string `haystack` contains the ASCII substring +// `needle`, ignoring case in the comparison. +bool StrContainsIgnoreCase(absl::string_view haystack, + absl::string_view needle) noexcept; + +bool StrContainsIgnoreCase(absl::string_view haystack, + char needle) noexcept; // EqualsIgnoreCase() // // Returns whether given ASCII strings `piece1` and `piece2` are equal, ignoring // case in the comparison. -bool EqualsIgnoreCase(absl::string_view piece1, absl::string_view piece2); +bool EqualsIgnoreCase(absl::string_view piece1, + absl::string_view piece2) noexcept; // StartsWithIgnoreCase() // // Returns whether a given ASCII string `text` starts with `prefix`, // ignoring case in the comparison. -bool StartsWithIgnoreCase(absl::string_view text, absl::string_view prefix); +bool StartsWithIgnoreCase(absl::string_view text, + absl::string_view prefix) noexcept; // EndsWithIgnoreCase() // // Returns whether a given ASCII string `text` ends with `suffix`, ignoring // case in the comparison. -bool EndsWithIgnoreCase(absl::string_view text, absl::string_view suffix); +bool EndsWithIgnoreCase(absl::string_view text, + absl::string_view suffix) noexcept; + +// Yields the longest prefix in common between both input strings. +// Pointer-wise, the returned result is a subset of input "a". +absl::string_view FindLongestCommonPrefix(absl::string_view a, + absl::string_view b); + +// Yields the longest suffix in common between both input strings. +// Pointer-wise, the returned result is a subset of input "a". +absl::string_view FindLongestCommonSuffix(absl::string_view a, + absl::string_view b); ABSL_NAMESPACE_END } // namespace absl diff --git a/Siv3D/include/ThirdParty/absl/strings/numbers.h b/Siv3D/include/ThirdParty/absl/strings/numbers.h index d872cca5dc..d7630cef9c 100644 --- a/Siv3D/include/ThirdParty/absl/strings/numbers.h +++ b/Siv3D/include/ThirdParty/absl/strings/numbers.h @@ -1,4 +1,3 @@ -// // Copyright 2017 The Abseil Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -24,8 +23,12 @@ #ifndef ABSL_STRINGS_NUMBERS_H_ #define ABSL_STRINGS_NUMBERS_H_ -#ifdef __SSE4_2__ -#include +#ifdef __SSSE3__ +#include +#endif + +#ifdef _MSC_VER +#include #endif #include @@ -37,17 +40,10 @@ #include #include "absl/base/config.h" -#include "absl/base/internal/bits.h" -#ifdef __SSE4_2__ -// TODO(jorg): Remove this when we figure out the right way -// to swap bytes on SSE 4.2 that works with the compilers -// we claim to support. Also, add tests for the compiler -// that doesn't support the Intel _bswap64 intrinsic but -// does support all the SSE 4.2 intrinsics #include "absl/base/internal/endian.h" -#endif #include "absl/base/macros.h" #include "absl/base/port.h" +#include "absl/numeric/bits.h" #include "absl/numeric/int128.h" #include "absl/strings/string_view.h" @@ -97,6 +93,25 @@ ABSL_MUST_USE_RESULT bool SimpleAtod(absl::string_view str, double* out); // unspecified state. ABSL_MUST_USE_RESULT bool SimpleAtob(absl::string_view str, bool* out); +// SimpleHexAtoi() +// +// Converts a hexadecimal string (optionally followed or preceded by ASCII +// whitespace) to an integer, returning `true` if successful. Only valid base-16 +// hexadecimal integers whose value falls within the range of the integer type +// (optionally preceded by a `+` or `-`) can be converted. A valid hexadecimal +// value may include both upper and lowercase character symbols, and may +// optionally include a leading "0x" (or "0X") number prefix, which is ignored +// by this function. If any errors are encountered, this function returns +// `false`, leaving `out` in an unspecified state. +template +ABSL_MUST_USE_RESULT bool SimpleHexAtoi(absl::string_view str, int_type* out); + +// Overloads of SimpleHexAtoi() for 128 bit integers. +ABSL_MUST_USE_RESULT inline bool SimpleHexAtoi(absl::string_view str, + absl::int128* out); +ABSL_MUST_USE_RESULT inline bool SimpleHexAtoi(absl::string_view str, + absl::uint128* out); + ABSL_NAMESPACE_END } // namespace absl @@ -110,8 +125,6 @@ namespace numbers_internal { ABSL_DLL extern const char kHexChar[17]; // 0123456789abcdef ABSL_DLL extern const char kHexTable[513]; // 000102030405060708090a0b0c0d0e0f1011... -ABSL_DLL extern const char - two_ASCII_digits[100][2]; // 00, 01, 02, 03... // Writes a two-character representation of 'i' to 'buf'. 'i' must be in the // range 0 <= i < 100, and buf must have space for two characters. Example: @@ -119,14 +132,14 @@ ABSL_DLL extern const char // PutTwoDigits(42, buf); // // buf[0] == '4' // // buf[1] == '2' -inline void PutTwoDigits(size_t i, char* buf) { - assert(i < 100); - memcpy(buf, two_ASCII_digits[i], 2); -} +void PutTwoDigits(uint32_t i, char* buf); // safe_strto?() functions for implementing SimpleAtoi() + bool safe_strto32_base(absl::string_view text, int32_t* value, int base); bool safe_strto64_base(absl::string_view text, int64_t* value, int base); +bool safe_strto128_base(absl::string_view text, absl::int128* value, + int base); bool safe_strtou32_base(absl::string_view text, uint32_t* value, int base); bool safe_strtou64_base(absl::string_view text, uint64_t* value, int base); bool safe_strtou128_base(absl::string_view text, absl::uint128* value, @@ -160,16 +173,19 @@ char* FastIntToBuffer(int_type i, char* buffer) { // TODO(jorg): This signed-ness check is used because it works correctly // with enums, and it also serves to check that int_type is not a pointer. // If one day something like std::is_signed works, switch to it. - if (static_cast(1) - 2 < 0) { // Signed - if (sizeof(i) > 32 / 8) { // 33-bit to 64-bit + // These conditions are constexpr bools to suppress MSVC warning C4127. + constexpr bool kIsSigned = static_cast(1) - 2 < 0; + constexpr bool kUse64Bit = sizeof(i) > 32 / 8; + if (kIsSigned) { + if (kUse64Bit) { return FastIntToBuffer(static_cast(i), buffer); - } else { // 32-bit or less + } else { return FastIntToBuffer(static_cast(i), buffer); } - } else { // Unsigned - if (sizeof(i) > 32 / 8) { // 33-bit to 64-bit + } else { + if (kUse64Bit) { return FastIntToBuffer(static_cast(i), buffer); - } else { // 32-bit or less + } else { return FastIntToBuffer(static_cast(i), buffer); } } @@ -188,22 +204,25 @@ ABSL_MUST_USE_RESULT bool safe_strtoi_base(absl::string_view s, int_type* out, // TODO(jorg): This signed-ness check is used because it works correctly // with enums, and it also serves to check that int_type is not a pointer. // If one day something like std::is_signed works, switch to it. - if (static_cast(1) - 2 < 0) { // Signed - if (sizeof(*out) == 64 / 8) { // 64-bit + // These conditions are constexpr bools to suppress MSVC warning C4127. + constexpr bool kIsSigned = static_cast(1) - 2 < 0; + constexpr bool kUse64Bit = sizeof(*out) == 64 / 8; + if (kIsSigned) { + if (kUse64Bit) { int64_t val; parsed = numbers_internal::safe_strto64_base(s, &val, base); *out = static_cast(val); - } else { // 32-bit + } else { int32_t val; parsed = numbers_internal::safe_strto32_base(s, &val, base); *out = static_cast(val); } - } else { // Unsigned - if (sizeof(*out) == 64 / 8) { // 64-bit + } else { + if (kUse64Bit) { uint64_t val; parsed = numbers_internal::safe_strtou64_base(s, &val, base); *out = static_cast(val); - } else { // 32-bit + } else { uint32_t val; parsed = numbers_internal::safe_strtou32_base(s, &val, base); *out = static_cast(val); @@ -219,7 +238,7 @@ ABSL_MUST_USE_RESULT bool safe_strtoi_base(absl::string_view s, int_type* out, // Returns the number of non-pad digits of the output (it can never be zero // since 0 has one digit). inline size_t FastHexToBufferZeroPad16(uint64_t val, char* out) { -#ifdef __SSE4_2__ +#ifdef ABSL_INTERNAL_HAVE_SSSE3 uint64_t be = absl::big_endian::FromHost64(val); const auto kNibbleMask = _mm_set1_epi8(0xf); const auto kHexDigits = _mm_setr_epi8('0', '1', '2', '3', '4', '5', '6', '7', @@ -238,28 +257,41 @@ inline size_t FastHexToBufferZeroPad16(uint64_t val, char* out) { } #endif // | 0x1 so that even 0 has 1 digit. - return 16 - absl::base_internal::CountLeadingZeros64(val | 0x1) / 4; + return 16 - static_cast(countl_zero(val | 0x1) / 4); } } // namespace numbers_internal -// SimpleAtoi() -// -// Converts a string to an integer, using `safe_strto?()` functions for actual -// parsing, returning `true` if successful. The `safe_strto?()` functions apply -// strict checking; the string must be a base-10 integer, optionally followed or -// preceded by ASCII whitespace, with a value in the range of the corresponding -// integer type. template ABSL_MUST_USE_RESULT bool SimpleAtoi(absl::string_view str, int_type* out) { return numbers_internal::safe_strtoi_base(str, out, 10); } +ABSL_MUST_USE_RESULT inline bool SimpleAtoi(absl::string_view str, + absl::int128* out) { + return numbers_internal::safe_strto128_base(str, out, 10); +} + ABSL_MUST_USE_RESULT inline bool SimpleAtoi(absl::string_view str, absl::uint128* out) { return numbers_internal::safe_strtou128_base(str, out, 10); } +template +ABSL_MUST_USE_RESULT bool SimpleHexAtoi(absl::string_view str, int_type* out) { + return numbers_internal::safe_strtoi_base(str, out, 16); +} + +ABSL_MUST_USE_RESULT inline bool SimpleHexAtoi(absl::string_view str, + absl::int128* out) { + return numbers_internal::safe_strto128_base(str, out, 16); +} + +ABSL_MUST_USE_RESULT inline bool SimpleHexAtoi(absl::string_view str, + absl::uint128* out) { + return numbers_internal::safe_strtou128_base(str, out, 16); +} + ABSL_NAMESPACE_END } // namespace absl diff --git a/Siv3D/include/ThirdParty/absl/strings/str_cat.h b/Siv3D/include/ThirdParty/absl/strings/str_cat.h index a8a85c7322..d5f71ff003 100644 --- a/Siv3D/include/ThirdParty/absl/strings/str_cat.h +++ b/Siv3D/include/ThirdParty/absl/strings/str_cat.h @@ -48,18 +48,58 @@ // `StrCat()` or `StrAppend()`. You may specify a minimum hex field width using // a `PadSpec` enum. // +// User-defined types can be formatted with the `AbslStringify()` customization +// point. The API relies on detecting an overload in the user-defined type's +// namespace of a free (non-member) `AbslStringify()` function as a definition +// (typically declared as a friend and implemented in-line. +// with the following signature: +// +// class MyClass { ... }; +// +// template +// void AbslStringify(Sink& sink, const MyClass& value); +// +// An `AbslStringify()` overload for a type should only be declared in the same +// file and namespace as said type. +// +// Note that `AbslStringify()` also supports use with `absl::StrFormat()` and +// `absl::Substitute()`. +// +// Example: +// +// struct Point { +// // To add formatting support to `Point`, we simply need to add a free +// // (non-member) function `AbslStringify()`. This method specifies how +// // Point should be printed when absl::StrCat() is called on it. You can add +// // such a free function using a friend declaration within the body of the +// // class. The sink parameter is a templated type to avoid requiring +// // dependencies. +// template friend void AbslStringify(Sink& +// sink, const Point& p) { +// absl::Format(&sink, "(%v, %v)", p.x, p.y); +// } +// +// int x; +// int y; +// }; // ----------------------------------------------------------------------------- #ifndef ABSL_STRINGS_STR_CAT_H_ #define ABSL_STRINGS_STR_CAT_H_ +#include #include #include +#include #include #include +#include #include +#include "absl/base/attributes.h" #include "absl/base/port.h" +#include "absl/strings/internal/has_absl_stringify.h" +#include "absl/strings/internal/stringify_sink.h" #include "absl/strings/numbers.h" #include "absl/strings/string_view.h" @@ -164,6 +204,27 @@ struct Hex { explicit Hex(Pointee* v, PadSpec spec = absl::kNoPad) : Hex(spec, reinterpret_cast(v)) {} + template + friend void AbslStringify(S& sink, Hex hex) { + static_assert( + numbers_internal::kFastToBufferSize >= 32, + "This function only works when output buffer >= 32 bytes long"); + char buffer[numbers_internal::kFastToBufferSize]; + char* const end = &buffer[numbers_internal::kFastToBufferSize]; + auto real_width = + absl::numbers_internal::FastHexToBufferZeroPad16(hex.value, end - 16); + if (real_width >= hex.width) { + sink.Append(absl::string_view(end - real_width, real_width)); + } else { + // Pad first 16 chars because FastHexToBufferZeroPad16 pads only to 16 and + // max pad width can be up to 20. + std::memset(end - 32, hex.fill, 16); + // Patch up everything else up to the real_width. + std::memset(end - real_width - 16, hex.fill, 16); + sink.Append(absl::string_view(end - hex.width, hex.width)); + } + } + private: Hex(PadSpec spec, uint64_t v) : value(v), @@ -198,6 +259,38 @@ struct Dec { : spec - absl::kZeroPad2 + 2), fill(spec >= absl::kSpacePad2 ? ' ' : '0'), neg(v < 0) {} + + template + friend void AbslStringify(S& sink, Dec dec) { + assert(dec.width <= numbers_internal::kFastToBufferSize); + char buffer[numbers_internal::kFastToBufferSize]; + char* const end = &buffer[numbers_internal::kFastToBufferSize]; + char* const minfill = end - dec.width; + char* writer = end; + uint64_t val = dec.value; + while (val > 9) { + *--writer = '0' + (val % 10); + val /= 10; + } + *--writer = '0' + static_cast(val); + if (dec.neg) *--writer = '-'; + + ptrdiff_t fillers = writer - minfill; + if (fillers > 0) { + // Tricky: if the fill character is ' ', then it's <+/-> + // But...: if the fill character is '0', then it's <+/-> + bool add_sign_again = false; + if (dec.neg && dec.fill == '0') { // If filling with '0', + ++writer; // ignore the sign we just added + add_sign_again = true; // and re-add the sign later. + } + writer -= fillers; + std::fill_n(writer, fillers, dec.fill); + if (add_sign_again) *--writer = '-'; + } + + sink.Append(absl::string_view(writer, static_cast(end - writer))); + } }; // ----------------------------------------------------------------------------- @@ -205,8 +298,10 @@ struct Dec { // ----------------------------------------------------------------------------- // // The `AlphaNum` class acts as the main parameter type for `StrCat()` and -// `StrAppend()`, providing efficient conversion of numeric, boolean, and -// hexadecimal values (through the `Hex` type) into strings. +// `StrAppend()`, providing efficient conversion of numeric, boolean, decimal, +// and hexadecimal values (through the `Dec` and `Hex` types) into strings. +// `AlphaNum` should only be used as a function parameter. Do not instantiate +// `AlphaNum` directly as a stack variable. class AlphaNum { public: @@ -214,43 +309,59 @@ class AlphaNum { // A bool ctor would also convert incoming pointers (bletch). AlphaNum(int x) // NOLINT(runtime/explicit) - : piece_(digits_, - numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {} + : piece_(digits_, static_cast( + numbers_internal::FastIntToBuffer(x, digits_) - + &digits_[0])) {} AlphaNum(unsigned int x) // NOLINT(runtime/explicit) - : piece_(digits_, - numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {} + : piece_(digits_, static_cast( + numbers_internal::FastIntToBuffer(x, digits_) - + &digits_[0])) {} AlphaNum(long x) // NOLINT(*) - : piece_(digits_, - numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {} + : piece_(digits_, static_cast( + numbers_internal::FastIntToBuffer(x, digits_) - + &digits_[0])) {} AlphaNum(unsigned long x) // NOLINT(*) - : piece_(digits_, - numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {} + : piece_(digits_, static_cast( + numbers_internal::FastIntToBuffer(x, digits_) - + &digits_[0])) {} AlphaNum(long long x) // NOLINT(*) - : piece_(digits_, - numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {} + : piece_(digits_, static_cast( + numbers_internal::FastIntToBuffer(x, digits_) - + &digits_[0])) {} AlphaNum(unsigned long long x) // NOLINT(*) - : piece_(digits_, - numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {} + : piece_(digits_, static_cast( + numbers_internal::FastIntToBuffer(x, digits_) - + &digits_[0])) {} AlphaNum(float f) // NOLINT(runtime/explicit) : piece_(digits_, numbers_internal::SixDigitsToBuffer(f, digits_)) {} AlphaNum(double f) // NOLINT(runtime/explicit) : piece_(digits_, numbers_internal::SixDigitsToBuffer(f, digits_)) {} - AlphaNum(Hex hex); // NOLINT(runtime/explicit) - AlphaNum(Dec dec); // NOLINT(runtime/explicit) - template AlphaNum( // NOLINT(runtime/explicit) - const strings_internal::AlphaNumBuffer& buf) + const strings_internal::AlphaNumBuffer& buf + ABSL_ATTRIBUTE_LIFETIME_BOUND) : piece_(&buf.data[0], buf.size) {} - AlphaNum(const char* c_str) : piece_(c_str) {} // NOLINT(runtime/explicit) - AlphaNum(absl::string_view pc) : piece_(pc) {} // NOLINT(runtime/explicit) + AlphaNum(const char* c_str // NOLINT(runtime/explicit) + ABSL_ATTRIBUTE_LIFETIME_BOUND) + : piece_(NullSafeStringView(c_str)) {} + AlphaNum(absl::string_view pc // NOLINT(runtime/explicit) + ABSL_ATTRIBUTE_LIFETIME_BOUND) + : piece_(pc) {} + + template ::value>::type> + AlphaNum( // NOLINT(runtime/explicit) + const T& v ABSL_ATTRIBUTE_LIFETIME_BOUND, + strings_internal::StringifySink&& sink ABSL_ATTRIBUTE_LIFETIME_BOUND = {}) + : piece_(strings_internal::ExtractStringification(sink, v)) {} template AlphaNum( // NOLINT(runtime/explicit) - const std::basic_string, Allocator>& str) + const std::basic_string, Allocator>& str + ABSL_ATTRIBUTE_LIFETIME_BOUND) : piece_(str) {} // Use string literals ":" instead of character literals ':'. @@ -263,13 +374,24 @@ class AlphaNum { const char* data() const { return piece_.data(); } absl::string_view Piece() const { return piece_; } - // Normal enums are already handled by the integer formatters. - // This overload matches only scoped enums. + // Match unscoped enums. Use integral promotion so that a `char`-backed + // enum becomes a wider integral type AlphaNum will accept. template {} && !std::is_convertible{}>::type> + std::is_enum{} && std::is_convertible{} && + !strings_internal::HasAbslStringify::value>::type> + AlphaNum(T e) // NOLINT(runtime/explicit) + : AlphaNum(+e) {} + + // This overload matches scoped enums. We must explicitly cast to the + // underlying type, but use integral promotion for the same reason as above. + template {} && !std::is_convertible{} && + !strings_internal::HasAbslStringify::value, + char*>::type = nullptr> AlphaNum(T e) // NOLINT(runtime/explicit) - : AlphaNum(static_cast::type>(e)) {} + : AlphaNum(+static_cast::type>(e)) {} // vector::reference and const_reference require special help to // convert to `AlphaNum` because it requires two user defined conversions. diff --git a/Siv3D/include/ThirdParty/absl/strings/str_format.h b/Siv3D/include/ThirdParty/absl/strings/str_format.h index 01465107e1..023e4350d3 100644 --- a/Siv3D/include/ThirdParty/absl/strings/str_format.h +++ b/Siv3D/include/ThirdParty/absl/strings/str_format.h @@ -36,10 +36,12 @@ // * `absl::StreamFormat()` to more efficiently write a format string to a // stream, such as`std::cout`. // * `absl::PrintF()`, `absl::FPrintF()` and `absl::SNPrintF()` as -// replacements for `std::printf()`, `std::fprintf()` and `std::snprintf()`. +// drop-in replacements for `std::printf()`, `std::fprintf()` and +// `std::snprintf()`. // -// Note: a version of `std::sprintf()` is not supported as it is -// generally unsafe due to buffer overflows. +// Note: An `absl::SPrintF()` drop-in replacement is not supported as it +// is generally unsafe due to buffer overflows. Use `absl::StrFormat` which +// returns the string as output instead of expecting a pre-allocated buffer. // // Additionally, you can provide a format string (and its associated arguments) // using one of the following abstractions: @@ -191,9 +193,9 @@ class FormatCountCapture { // absl::StrFormat(formatString, "TheVillage", 6); // // A format string generally follows the POSIX syntax as used within the POSIX -// `printf` specification. +// `printf` specification. (Exceptions are noted below.) // -// (See http://pubs.opengroup.org/onlinepubs/9699919799/functions/fprintf.html.) +// (See http://pubs.opengroup.org/onlinepubs/9699919799/functions/fprintf.html) // // In specific, the `FormatSpec` supports the following type specifiers: // * `c` for characters @@ -211,6 +213,10 @@ class FormatCountCapture { // * `n` for the special case of writing out the number of characters // written to this point. The resulting value must be captured within an // `absl::FormatCountCapture` type. +// * `v` for values using the default format for a deduced type. These deduced +// types include many of the primitive types denoted here as well as +// user-defined types containing the proper extensions. (See below for more +// information.) // // Implementation-defined behavior: // * A null pointer provided to "%s" or "%p" is output as "(nil)". @@ -239,11 +245,21 @@ class FormatCountCapture { // "%s%d%n", "hello", 123, absl::FormatCountCapture(&n)); // EXPECT_EQ(8, n); // +// NOTE: the `v` specifier (for "value") is a type specifier not present in the +// POSIX specification. %v will format values according to their deduced type. +// `v` uses `d` for signed integer values, `u` for unsigned integer values, `g` +// for floating point values, and formats boolean values as "true"/"false" +// (instead of 1 or 0 for booleans formatted using d). `const char*` is not +// supported; please use `std:string` and `string_view`. `char` is also not +// supported due to ambiguity of the type. This specifier does not support +// modifiers. +// // The `FormatSpec` intrinsically supports all of these fundamental C++ types: // // * Characters: `char`, `signed char`, `unsigned char` // * Integers: `int`, `short`, `unsigned short`, `unsigned`, `long`, // `unsigned long`, `long long`, `unsigned long long` +// * Enums: printed as their underlying integral value // * Floating-point: `float`, `double`, `long double` // // However, in the `str_format` library, a format conversion specifies a broader @@ -536,8 +552,7 @@ using FormatArg = str_format_internal::FormatArgImpl; // The arguments are provided in an `absl::Span`. // Each `absl::FormatArg` object binds to a single argument and keeps a // reference to it. The values used to create the `FormatArg` objects must -// outlive this function call. (See `str_format_arg.h` for information on -// the `FormatArg` class.)_ +// outlive this function call. // // Example: // @@ -571,6 +586,41 @@ ABSL_MUST_USE_RESULT inline bool FormatUntyped( // StrFormat Extensions //------------------------------------------------------------------------------ // +// AbslStringify() +// +// A simpler customization API for formatting user-defined types using +// absl::StrFormat(). The API relies on detecting an overload in the +// user-defined type's namespace of a free (non-member) `AbslStringify()` +// function as a friend definition with the following signature: +// +// template +// void AbslStringify(Sink& sink, const X& value); +// +// An `AbslStringify()` overload for a type should only be declared in the same +// file and namespace as said type. +// +// Note that unlike with AbslFormatConvert(), AbslStringify() does not allow +// customization of allowed conversion characters. AbslStringify() uses `%v` as +// the underlying conversion specififer. Additionally, AbslStringify() supports +// use with absl::StrCat while AbslFormatConvert() does not. +// +// Example: +// +// struct Point { +// // To add formatting support to `Point`, we simply need to add a free +// // (non-member) function `AbslStringify()`. This method prints in the +// // request format using the underlying `%v` specifier. You can add such a +// // free function using a friend declaration within the body of the class. +// // The sink parameter is a templated type to avoid requiring dependencies. +// template +// friend void AbslStringify(Sink& sink, const Point& p) { +// absl::Format(&sink, "(%v, %v)", p.x, p.y); +// } +// +// int x; +// int y; +// }; +// // AbslFormatConvert() // // The StrFormat library provides a customization API for formatting @@ -617,9 +667,9 @@ ABSL_MUST_USE_RESULT inline bool FormatUntyped( // AbslFormatConvert(const Point& p, const absl::FormatConversionSpec& spec, // absl::FormatSink* s) { // if (spec.conversion_char() == absl::FormatConversionChar::s) { -// s->Append(absl::StrCat("x=", p.x, " y=", p.y)); +// absl::Format(s, "x=%vy=%v", p.x, p.y); // } else { -// s->Append(absl::StrCat(p.x, ",", p.y)); +// absl::Format(s, "%v,%v", p.x, p.y); // } // return {true}; // } @@ -638,7 +688,7 @@ enum class FormatConversionChar : uint8_t { c, s, // text d, i, o, u, x, X, // int f, F, e, E, g, G, a, A, // float - n, p // misc + n, p, v // misc }; // clang-format on @@ -758,6 +808,7 @@ enum class FormatConversionCharSet : uint64_t { // misc n = str_format_internal::FormatConversionCharToConvInt('n'), p = str_format_internal::FormatConversionCharToConvInt('p'), + v = str_format_internal::FormatConversionCharToConvInt('v'), // Used for width/precision '*' specification. kStar = static_cast( @@ -772,23 +823,36 @@ enum class FormatConversionCharSet : uint64_t { // FormatSink // -// An abstraction to which conversions write their string data. +// A format sink is a generic abstraction to which conversions may write their +// formatted string data. `absl::FormatConvert()` uses this sink to write its +// formatted string. // class FormatSink { public: - // Appends `count` copies of `ch`. + // FormatSink::Append() + // + // Appends `count` copies of `ch` to the format sink. void Append(size_t count, char ch) { sink_->Append(count, ch); } + // Overload of FormatSink::Append() for appending the characters of a string + // view to a format sink. void Append(string_view v) { sink_->Append(v); } - // Appends the first `precision` bytes of `v`. If this is less than - // `width`, spaces will be appended first (if `left` is false), or + // FormatSink::PutPaddedString() + // + // Appends `precision` number of bytes of `v` to the format sink. If this is + // less than `width`, spaces will be appended first (if `left` is false), or // after (if `left` is true) to ensure the total amount appended is // at least `width`. bool PutPaddedString(string_view v, int width, int precision, bool left) { return sink_->PutPaddedString(v, width, precision, left); } + // Support `absl::Format(&sink, format, args...)`. + friend void AbslFormatFlush(FormatSink* sink, absl::string_view v) { + sink->Append(v); + } + private: friend str_format_internal::FormatSinkImpl; explicit FormatSink(str_format_internal::FormatSinkImpl* s) : sink_(s) {} diff --git a/Siv3D/include/ThirdParty/absl/strings/str_join.h b/Siv3D/include/ThirdParty/absl/strings/str_join.h index ae5731a42b..ee5ae7efdf 100644 --- a/Siv3D/include/ThirdParty/absl/strings/str_join.h +++ b/Siv3D/include/ThirdParty/absl/strings/str_join.h @@ -72,21 +72,15 @@ ABSL_NAMESPACE_BEGIN // functions. You may provide your own Formatter to enable `absl::StrJoin()` to // work with arbitrary types. // -// The following is an example of a custom Formatter that simply uses -// `std::to_string()` to format an integer as a std::string. +// The following is an example of a custom Formatter that uses +// `absl::FormatDuration` to join a list of `absl::Duration`s. // -// struct MyFormatter { -// void operator()(std::string* out, int i) const { -// out->append(std::to_string(i)); -// } -// }; -// -// You would use the above formatter by passing an instance of it as the final -// argument to `absl::StrJoin()`: -// -// std::vector v = {1, 2, 3, 4}; -// std::string s = absl::StrJoin(v, "-", MyFormatter()); -// EXPECT_EQ("1-2-3-4", s); +// std::vector v = {absl::Seconds(1), absl::Milliseconds(10)}; +// std::string s = +// absl::StrJoin(v, ", ", [](std::string* out, absl::Duration dur) { +// absl::StrAppend(out, absl::FormatDuration(dur)); +// }); +// EXPECT_EQ("1s, 10ms", s); // // The following standard formatters are provided within this file: // @@ -144,7 +138,7 @@ strings_internal::DereferenceFormatterImpl DereferenceFormatter( std::forward(f)); } -// Function overload of `DererefenceFormatter()` for using a default +// Function overload of `DereferenceFormatter()` for using a default // `AlphaNumFormatter()`. inline strings_internal::DereferenceFormatterImpl< strings_internal::AlphaNumFormatterImpl> diff --git a/Siv3D/include/ThirdParty/absl/strings/str_split.h b/Siv3D/include/ThirdParty/absl/strings/str_split.h index 1ce17f38aa..7bbb68a343 100644 --- a/Siv3D/include/ThirdParty/absl/strings/str_split.h +++ b/Siv3D/include/ThirdParty/absl/strings/str_split.h @@ -369,6 +369,12 @@ struct SkipWhitespace { } }; +template +using EnableSplitIfString = + typename std::enable_if::value || + std::is_same::value, + int>::type; + //------------------------------------------------------------------------------ // StrSplit() //------------------------------------------------------------------------------ @@ -455,8 +461,7 @@ struct SkipWhitespace { // first two split strings become the `std::pair` `.first` and `.second` // members, respectively. The remaining split substrings are discarded. If there // are less than two split substrings, the empty string is used for the -// corresponding -// `std::pair` member. +// corresponding `std::pair` member. // // Example: // @@ -489,22 +494,50 @@ struct SkipWhitespace { // Try not to depend on this distinction because the bug may one day be fixed. template strings_internal::Splitter< - typename strings_internal::SelectDelimiter::type, AllowEmpty> + typename strings_internal::SelectDelimiter::type, AllowEmpty, + absl::string_view> StrSplit(strings_internal::ConvertibleToStringView text, Delimiter d) { using DelimiterType = typename strings_internal::SelectDelimiter::type; - return strings_internal::Splitter( + return strings_internal::Splitter( + text.value(), DelimiterType(d), AllowEmpty()); +} + +template = 0> +strings_internal::Splitter< + typename strings_internal::SelectDelimiter::type, AllowEmpty, + std::string> +StrSplit(StringType&& text, Delimiter d) { + using DelimiterType = + typename strings_internal::SelectDelimiter::type; + return strings_internal::Splitter( std::move(text), DelimiterType(d), AllowEmpty()); } template strings_internal::Splitter< - typename strings_internal::SelectDelimiter::type, Predicate> + typename strings_internal::SelectDelimiter::type, Predicate, + absl::string_view> StrSplit(strings_internal::ConvertibleToStringView text, Delimiter d, Predicate p) { using DelimiterType = typename strings_internal::SelectDelimiter::type; - return strings_internal::Splitter( + return strings_internal::Splitter( + text.value(), DelimiterType(d), std::move(p)); +} + +template = 0> +strings_internal::Splitter< + typename strings_internal::SelectDelimiter::type, Predicate, + std::string> +StrSplit(StringType&& text, Delimiter d, Predicate p) { + using DelimiterType = + typename strings_internal::SelectDelimiter::type; + return strings_internal::Splitter( std::move(text), DelimiterType(d), std::move(p)); } diff --git a/Siv3D/include/ThirdParty/absl/strings/string_view.h b/Siv3D/include/ThirdParty/absl/strings/string_view.h index 5260b5b73f..eae11b2ab6 100644 --- a/Siv3D/include/ThirdParty/absl/strings/string_view.h +++ b/Siv3D/include/ThirdParty/absl/strings/string_view.h @@ -36,6 +36,7 @@ #include #include +#include "absl/base/attributes.h" #include "absl/base/config.h" #include "absl/base/internal/throw_delegate.h" #include "absl/base/macros.h" @@ -54,8 +55,9 @@ ABSL_NAMESPACE_END #else // ABSL_USES_STD_STRING_VIEW -#if ABSL_HAVE_BUILTIN(__builtin_memcmp) || \ - (defined(__GNUC__) && !defined(__clang__)) +#if ABSL_HAVE_BUILTIN(__builtin_memcmp) || \ + (defined(__GNUC__) && !defined(__clang__)) || \ + (defined(_MSC_VER) && _MSC_VER >= 1928) #define ABSL_INTERNAL_STRING_VIEW_MEMCMP __builtin_memcmp #else // ABSL_HAVE_BUILTIN(__builtin_memcmp) #define ABSL_INTERNAL_STRING_VIEW_MEMCMP memcmp @@ -180,18 +182,20 @@ class string_view { template string_view( // NOLINT(runtime/explicit) - const std::basic_string, Allocator>& - str) noexcept + const std::basic_string, Allocator>& str + ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept // This is implemented in terms of `string_view(p, n)` so `str.size()` // doesn't need to be reevaluated after `ptr_` is set. - : string_view(str.data(), str.size()) {} + // The length check is also skipped since it is unnecessary and causes + // code bloat. + : string_view(str.data(), str.size(), SkipCheckLengthTag{}) {} // Implicit constructor of a `string_view` from NUL-terminated `str`. When // accepting possibly null strings, use `absl::NullSafeStringView(str)` // instead (see below). + // The length check is skipped since it is unnecessary and causes code bloat. constexpr string_view(const char* str) // NOLINT(runtime/explicit) - : ptr_(str), - length_(str ? CheckLengthInternal(StrlenInternal(str)) : 0) {} + : ptr_(str), length_(str ? StrlenInternal(str) : 0) {} // Implicit constructor of a `string_view` from a `const char*` and length. constexpr string_view(const char* data, size_type len) @@ -264,9 +268,7 @@ class string_view { // string_view::size() // // Returns the number of characters in the `string_view`. - constexpr size_type size() const noexcept { - return length_; - } + constexpr size_type size() const noexcept { return length_; } // string_view::length() // @@ -333,7 +335,7 @@ class string_view { // // Removes the first `n` characters from the `string_view`. Note that the // underlying string is not changed, only the view. - void remove_prefix(size_type n) { + constexpr void remove_prefix(size_type n) { ABSL_HARDENING_ASSERT(n <= length_); ptr_ += n; length_ -= n; @@ -343,7 +345,7 @@ class string_view { // // Removes the last `n` characters from the `string_view`. Note that the // underlying string is not changed, only the view. - void remove_suffix(size_type n) { + constexpr void remove_suffix(size_type n) { ABSL_HARDENING_ASSERT(n <= length_); length_ -= n; } @@ -351,7 +353,7 @@ class string_view { // string_view::swap() // // Swaps this `string_view` with another `string_view`. - void swap(string_view& s) noexcept { + constexpr void swap(string_view& s) noexcept { auto t = *this; *this = s; s = t; @@ -388,7 +390,7 @@ class string_view { // `n`) as another string_view. This function throws `std::out_of_bounds` if // `pos > size`. // Use absl::ClippedSubstr if you need a truncating substr operation. - constexpr string_view substr(size_type pos, size_type n = npos) const { + constexpr string_view substr(size_type pos = 0, size_type n = npos) const { return ABSL_PREDICT_FALSE(pos > length_) ? (base_internal::ThrowStdOutOfRange( "absl::string_view::substr"), @@ -398,12 +400,10 @@ class string_view { // string_view::compare() // - // Performs a lexicographical comparison between the `string_view` and - // another `absl::string_view`, returning -1 if `this` is less than, 0 if - // `this` is equal to, and 1 if `this` is greater than the passed string - // view. Note that in the case of data equality, a further comparison is made - // on the respective sizes of the two `string_view`s to determine which is - // smaller, equal, or greater. + // Performs a lexicographical comparison between this `string_view` and + // another `string_view` `x`, returning a negative value if `*this` is less + // than `x`, 0 if `*this` is equal to `x`, and a positive value if `*this` + // is greater than `x`. constexpr int compare(string_view x) const noexcept { return CompareImpl(length_, x.length_, Min(length_, x.length_) == 0 @@ -414,31 +414,31 @@ class string_view { // Overload of `string_view::compare()` for comparing a substring of the // 'string_view` and another `absl::string_view`. - int compare(size_type pos1, size_type count1, string_view v) const { + constexpr int compare(size_type pos1, size_type count1, string_view v) const { return substr(pos1, count1).compare(v); } // Overload of `string_view::compare()` for comparing a substring of the // `string_view` and a substring of another `absl::string_view`. - int compare(size_type pos1, size_type count1, string_view v, size_type pos2, - size_type count2) const { + constexpr int compare(size_type pos1, size_type count1, string_view v, + size_type pos2, size_type count2) const { return substr(pos1, count1).compare(v.substr(pos2, count2)); } // Overload of `string_view::compare()` for comparing a `string_view` and a - // a different C-style string `s`. - int compare(const char* s) const { return compare(string_view(s)); } + // a different C-style string `s`. + constexpr int compare(const char* s) const { return compare(string_view(s)); } // Overload of `string_view::compare()` for comparing a substring of the // `string_view` and a different string C-style string `s`. - int compare(size_type pos1, size_type count1, const char* s) const { + constexpr int compare(size_type pos1, size_type count1, const char* s) const { return substr(pos1, count1).compare(string_view(s)); } // Overload of `string_view::compare()` for comparing a substring of the // `string_view` and a substring of a different C-style string `s`. - int compare(size_type pos1, size_type count1, const char* s, - size_type count2) const { + constexpr int compare(size_type pos1, size_type count1, const char* s, + size_type count2) const { return substr(pos1, count1).compare(string_view(s, count2)); } @@ -455,48 +455,92 @@ class string_view { // within the `string_view`. size_type find(char c, size_type pos = 0) const noexcept; + // Overload of `string_view::find()` for finding a substring of a different + // C-style string `s` within the `string_view`. + size_type find(const char* s, size_type pos, size_type count) const { + return find(string_view(s, count), pos); + } + + // Overload of `string_view::find()` for finding a different C-style string + // `s` within the `string_view`. + size_type find(const char* s, size_type pos = 0) const { + return find(string_view(s), pos); + } + // string_view::rfind() // // Finds the last occurrence of a substring `s` within the `string_view`, // returning the position of the first character's match, or `npos` if no // match was found. - size_type rfind(string_view s, size_type pos = npos) const - noexcept; + size_type rfind(string_view s, size_type pos = npos) const noexcept; // Overload of `string_view::rfind()` for finding the last given character `c` // within the `string_view`. size_type rfind(char c, size_type pos = npos) const noexcept; + // Overload of `string_view::rfind()` for finding a substring of a different + // C-style string `s` within the `string_view`. + size_type rfind(const char* s, size_type pos, size_type count) const { + return rfind(string_view(s, count), pos); + } + + // Overload of `string_view::rfind()` for finding a different C-style string + // `s` within the `string_view`. + size_type rfind(const char* s, size_type pos = npos) const { + return rfind(string_view(s), pos); + } + // string_view::find_first_of() // // Finds the first occurrence of any of the characters in `s` within the // `string_view`, returning the start position of the match, or `npos` if no // match was found. - size_type find_first_of(string_view s, size_type pos = 0) const - noexcept; + size_type find_first_of(string_view s, size_type pos = 0) const noexcept; // Overload of `string_view::find_first_of()` for finding a character `c` // within the `string_view`. - size_type find_first_of(char c, size_type pos = 0) const - noexcept { + size_type find_first_of(char c, size_type pos = 0) const noexcept { return find(c, pos); } + // Overload of `string_view::find_first_of()` for finding a substring of a + // different C-style string `s` within the `string_view`. + size_type find_first_of(const char* s, size_type pos, + size_type count) const { + return find_first_of(string_view(s, count), pos); + } + + // Overload of `string_view::find_first_of()` for finding a different C-style + // string `s` within the `string_view`. + size_type find_first_of(const char* s, size_type pos = 0) const { + return find_first_of(string_view(s), pos); + } + // string_view::find_last_of() // // Finds the last occurrence of any of the characters in `s` within the // `string_view`, returning the start position of the match, or `npos` if no // match was found. - size_type find_last_of(string_view s, size_type pos = npos) const - noexcept; + size_type find_last_of(string_view s, size_type pos = npos) const noexcept; // Overload of `string_view::find_last_of()` for finding a character `c` // within the `string_view`. - size_type find_last_of(char c, size_type pos = npos) const - noexcept { + size_type find_last_of(char c, size_type pos = npos) const noexcept { return rfind(c, pos); } + // Overload of `string_view::find_last_of()` for finding a substring of a + // different C-style string `s` within the `string_view`. + size_type find_last_of(const char* s, size_type pos, size_type count) const { + return find_last_of(string_view(s, count), pos); + } + + // Overload of `string_view::find_last_of()` for finding a different C-style + // string `s` within the `string_view`. + size_type find_last_of(const char* s, size_type pos = npos) const { + return find_last_of(string_view(s), pos); + } + // string_view::find_first_not_of() // // Finds the first occurrence of any of the characters not in `s` within the @@ -508,20 +552,51 @@ class string_view { // that is not `c` within the `string_view`. size_type find_first_not_of(char c, size_type pos = 0) const noexcept; + // Overload of `string_view::find_first_not_of()` for finding a substring of a + // different C-style string `s` within the `string_view`. + size_type find_first_not_of(const char* s, size_type pos, + size_type count) const { + return find_first_not_of(string_view(s, count), pos); + } + + // Overload of `string_view::find_first_not_of()` for finding a different + // C-style string `s` within the `string_view`. + size_type find_first_not_of(const char* s, size_type pos = 0) const { + return find_first_not_of(string_view(s), pos); + } + // string_view::find_last_not_of() // // Finds the last occurrence of any of the characters not in `s` within the // `string_view`, returning the start position of the last non-match, or // `npos` if no non-match was found. size_type find_last_not_of(string_view s, - size_type pos = npos) const noexcept; + size_type pos = npos) const noexcept; // Overload of `string_view::find_last_not_of()` for finding a character // that is not `c` within the `string_view`. - size_type find_last_not_of(char c, size_type pos = npos) const - noexcept; + size_type find_last_not_of(char c, size_type pos = npos) const noexcept; + + // Overload of `string_view::find_last_not_of()` for finding a substring of a + // different C-style string `s` within the `string_view`. + size_type find_last_not_of(const char* s, size_type pos, + size_type count) const { + return find_last_not_of(string_view(s, count), pos); + } + + // Overload of `string_view::find_last_not_of()` for finding a different + // C-style string `s` within the `string_view`. + size_type find_last_not_of(const char* s, size_type pos = npos) const { + return find_last_not_of(string_view(s), pos); + } private: + // The constructor from std::string delegates to this constructor. + // See the comment on that constructor for the rationale. + struct SkipCheckLengthTag {}; + string_view(const char* data, size_type len, SkipCheckLengthTag) noexcept + : ptr_(data), length_(len) {} + static constexpr size_type kMaxSize = (std::numeric_limits::max)(); diff --git a/Siv3D/include/ThirdParty/absl/strings/strip.h b/Siv3D/include/ThirdParty/absl/strings/strip.h index 111872ca54..341e66fc92 100644 --- a/Siv3D/include/ThirdParty/absl/strings/strip.h +++ b/Siv3D/include/ThirdParty/absl/strings/strip.h @@ -34,8 +34,9 @@ ABSL_NAMESPACE_BEGIN // ConsumePrefix() // -// Strips the `expected` prefix from the start of the given string, returning -// `true` if the strip operation succeeded or false otherwise. +// Strips the `expected` prefix, if found, from the start of `str`. +// If the operation succeeded, `true` is returned. If not, `false` +// is returned and `str` is not modified. // // Example: // @@ -49,8 +50,9 @@ inline bool ConsumePrefix(absl::string_view* str, absl::string_view expected) { } // ConsumeSuffix() // -// Strips the `expected` suffix from the end of the given string, returning -// `true` if the strip operation succeeded or false otherwise. +// Strips the `expected` suffix, if found, from the end of `str`. +// If the operation succeeded, `true` is returned. If not, `false` +// is returned and `str` is not modified. // // Example: // @@ -65,7 +67,7 @@ inline bool ConsumeSuffix(absl::string_view* str, absl::string_view expected) { // StripPrefix() // -// Returns a view into the input string 'str' with the given 'prefix' removed, +// Returns a view into the input string `str` with the given `prefix` removed, // but leaving the original string intact. If the prefix does not match at the // start of the string, returns the original string instead. ABSL_MUST_USE_RESULT inline absl::string_view StripPrefix( @@ -76,7 +78,7 @@ ABSL_MUST_USE_RESULT inline absl::string_view StripPrefix( // StripSuffix() // -// Returns a view into the input string 'str' with the given 'suffix' removed, +// Returns a view into the input string `str` with the given `suffix` removed, // but leaving the original string intact. If the suffix does not match at the // end of the string, returns the original string instead. ABSL_MUST_USE_RESULT inline absl::string_view StripSuffix( diff --git a/Siv3D/include/ThirdParty/absl/strings/substitute.h b/Siv3D/include/ThirdParty/absl/strings/substitute.h index c6da4dc6e7..d6a5a690d7 100644 --- a/Siv3D/include/ThirdParty/absl/strings/substitute.h +++ b/Siv3D/include/ThirdParty/absl/strings/substitute.h @@ -55,6 +55,8 @@ // * bool (Printed as "true" or "false") // * pointer types other than char* (Printed as "0x", // except that null is printed as "NULL") +// * user-defined types via the `AbslStringify()` customization point. See the +// documentation for `absl::StrCat` for an explanation on how to use this. // // If an invalid format string is provided, Substitute returns an empty string // and SubstituteAndAppend does not change the provided output string. @@ -79,6 +81,7 @@ #include "absl/base/port.h" #include "absl/strings/ascii.h" #include "absl/strings/escaping.h" +#include "absl/strings/internal/stringify_sink.h" #include "absl/strings/numbers.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_split.h" @@ -102,14 +105,14 @@ class Arg { // Overloads for string-y things // // Explicitly overload `const char*` so the compiler doesn't cast to `bool`. - Arg(const char* value) // NOLINT(runtime/explicit) + Arg(const char* value) // NOLINT(google-explicit-constructor) : piece_(absl::NullSafeStringView(value)) {} template Arg( // NOLINT const std::basic_string, Allocator>& value) noexcept : piece_(value) {} - Arg(absl::string_view value) // NOLINT(runtime/explicit) + Arg(absl::string_view value) // NOLINT(google-explicit-constructor) : piece_(value) {} // Overloads for primitives @@ -119,48 +122,70 @@ class Arg { // probably using them as 8-bit integers and would probably prefer an integer // representation. However, we can't really know, so we make the caller decide // what to do. - Arg(char value) // NOLINT(runtime/explicit) + Arg(char value) // NOLINT(google-explicit-constructor) : piece_(scratch_, 1) { scratch_[0] = value; } Arg(short value) // NOLINT(*) : piece_(scratch_, - numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {} + static_cast( + numbers_internal::FastIntToBuffer(value, scratch_) - + scratch_)) {} Arg(unsigned short value) // NOLINT(*) : piece_(scratch_, - numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {} - Arg(int value) // NOLINT(runtime/explicit) + static_cast( + numbers_internal::FastIntToBuffer(value, scratch_) - + scratch_)) {} + Arg(int value) // NOLINT(google-explicit-constructor) : piece_(scratch_, - numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {} - Arg(unsigned int value) // NOLINT(runtime/explicit) + static_cast( + numbers_internal::FastIntToBuffer(value, scratch_) - + scratch_)) {} + Arg(unsigned int value) // NOLINT(google-explicit-constructor) : piece_(scratch_, - numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {} + static_cast( + numbers_internal::FastIntToBuffer(value, scratch_) - + scratch_)) {} Arg(long value) // NOLINT(*) : piece_(scratch_, - numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {} + static_cast( + numbers_internal::FastIntToBuffer(value, scratch_) - + scratch_)) {} Arg(unsigned long value) // NOLINT(*) : piece_(scratch_, - numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {} + static_cast( + numbers_internal::FastIntToBuffer(value, scratch_) - + scratch_)) {} Arg(long long value) // NOLINT(*) : piece_(scratch_, - numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {} + static_cast( + numbers_internal::FastIntToBuffer(value, scratch_) - + scratch_)) {} Arg(unsigned long long value) // NOLINT(*) : piece_(scratch_, - numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {} - Arg(float value) // NOLINT(runtime/explicit) + static_cast( + numbers_internal::FastIntToBuffer(value, scratch_) - + scratch_)) {} + Arg(float value) // NOLINT(google-explicit-constructor) : piece_(scratch_, numbers_internal::SixDigitsToBuffer(value, scratch_)) { } - Arg(double value) // NOLINT(runtime/explicit) + Arg(double value) // NOLINT(google-explicit-constructor) : piece_(scratch_, numbers_internal::SixDigitsToBuffer(value, scratch_)) { } - Arg(bool value) // NOLINT(runtime/explicit) + Arg(bool value) // NOLINT(google-explicit-constructor) : piece_(value ? "true" : "false") {} - Arg(Hex hex); // NOLINT(runtime/explicit) - Arg(Dec dec); // NOLINT(runtime/explicit) + template ::value>::type> + Arg( // NOLINT(google-explicit-constructor) + const T& v, strings_internal::StringifySink&& sink = {}) + : piece_(strings_internal::ExtractStringification(sink, v)) {} - // vector::reference and const_reference require special help to - // convert to `AlphaNum` because it requires two user defined conversions. + Arg(Hex hex); // NOLINT(google-explicit-constructor) + Arg(Dec dec); // NOLINT(google-explicit-constructor) + + // vector::reference and const_reference require special help to convert + // to `Arg` because it requires two user defined conversions. template ::value && @@ -172,7 +197,16 @@ class Arg { // `void*` values, with the exception of `char*`, are printed as // "0x". However, in the case of `nullptr`, "NULL" is printed. - Arg(const void* value); // NOLINT(runtime/explicit) + Arg(const void* value); // NOLINT(google-explicit-constructor) + + // Normal enums are already handled by the integer formatters. + // This overload matches only scoped enums. + template {} && !std::is_convertible{} && + !strings_internal::HasAbslStringify::value>::type> + Arg(T value) // NOLINT(google-explicit-constructor) + : Arg(static_cast::type>(value)) {} Arg(const Arg&) = delete; Arg& operator=(const Arg&) = delete; @@ -361,43 +395,49 @@ inline void SubstituteAndAppend( // This body of functions catches cases where the number of placeholders // doesn't match the number of data arguments. void SubstituteAndAppend(std::string* output, const char* format) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 0, - "There were no substitution arguments " - "but this format string has a $[0-9] in it"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 0, + "There were no substitution arguments " + "but this format string either has a $[0-9] in it or contains " + "an unescaped $ character (use $$ instead)"); void SubstituteAndAppend(std::string* output, const char* format, const substitute_internal::Arg& a0) ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 1, "There was 1 substitution argument given, but " - "this format string is either missing its $0, or " - "contains one of $1-$9"); + "this format string is missing its $0, contains " + "one of $1-$9, or contains an unescaped $ character (use " + "$$ instead)"); void SubstituteAndAppend(std::string* output, const char* format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a1) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 3, - "There were 2 substitution arguments given, but " - "this format string is either missing its $0/$1, or " - "contains one of $2-$9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 3, + "There were 2 substitution arguments given, but this format string is " + "missing its $0/$1, contains one of $2-$9, or contains an " + "unescaped $ character (use $$ instead)"); void SubstituteAndAppend(std::string* output, const char* format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, const substitute_internal::Arg& a2) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 7, - "There were 3 substitution arguments given, but " - "this format string is either missing its $0/$1/$2, or " - "contains one of $3-$9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 7, + "There were 3 substitution arguments given, but " + "this format string is missing its $0/$1/$2, contains one of " + "$3-$9, or contains an unescaped $ character (use $$ instead)"); void SubstituteAndAppend(std::string* output, const char* format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, const substitute_internal::Arg& a2, const substitute_internal::Arg& a3) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 15, - "There were 4 substitution arguments given, but " - "this format string is either missing its $0-$3, or " - "contains one of $4-$9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 15, + "There were 4 substitution arguments given, but " + "this format string is missing its $0-$3, contains one of " + "$4-$9, or contains an unescaped $ character (use $$ instead)"); void SubstituteAndAppend(std::string* output, const char* format, const substitute_internal::Arg& a0, @@ -405,10 +445,11 @@ void SubstituteAndAppend(std::string* output, const char* format, const substitute_internal::Arg& a2, const substitute_internal::Arg& a3, const substitute_internal::Arg& a4) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 31, - "There were 5 substitution arguments given, but " - "this format string is either missing its $0-$4, or " - "contains one of $5-$9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 31, + "There were 5 substitution arguments given, but " + "this format string is missing its $0-$4, contains one of " + "$5-$9, or contains an unescaped $ character (use $$ instead)"); void SubstituteAndAppend(std::string* output, const char* format, const substitute_internal::Arg& a0, @@ -417,20 +458,22 @@ void SubstituteAndAppend(std::string* output, const char* format, const substitute_internal::Arg& a3, const substitute_internal::Arg& a4, const substitute_internal::Arg& a5) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 63, - "There were 6 substitution arguments given, but " - "this format string is either missing its $0-$5, or " - "contains one of $6-$9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 63, + "There were 6 substitution arguments given, but " + "this format string is missing its $0-$5, contains one of " + "$6-$9, or contains an unescaped $ character (use $$ instead)"); void SubstituteAndAppend( std::string* output, const char* format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, const substitute_internal::Arg& a2, const substitute_internal::Arg& a3, const substitute_internal::Arg& a4, const substitute_internal::Arg& a5, const substitute_internal::Arg& a6) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 127, - "There were 7 substitution arguments given, but " - "this format string is either missing its $0-$6, or " - "contains one of $7-$9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 127, + "There were 7 substitution arguments given, but " + "this format string is missing its $0-$6, contains one of " + "$7-$9, or contains an unescaped $ character (use $$ instead)"); void SubstituteAndAppend( std::string* output, const char* format, const substitute_internal::Arg& a0, @@ -438,10 +481,11 @@ void SubstituteAndAppend( const substitute_internal::Arg& a3, const substitute_internal::Arg& a4, const substitute_internal::Arg& a5, const substitute_internal::Arg& a6, const substitute_internal::Arg& a7) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 255, - "There were 8 substitution arguments given, but " - "this format string is either missing its $0-$7, or " - "contains one of $8-$9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 255, + "There were 8 substitution arguments given, but " + "this format string is missing its $0-$7, contains one of " + "$8-$9, or contains an unescaped $ character (use $$ instead)"); void SubstituteAndAppend( std::string* output, const char* format, const substitute_internal::Arg& a0, @@ -452,7 +496,8 @@ void SubstituteAndAppend( ABSL_BAD_CALL_IF( substitute_internal::PlaceholderBitmask(format) != 511, "There were 9 substitution arguments given, but " - "this format string is either missing its $0-$8, or contains a $9"); + "this format string is missing its $0-$8, contains a $9, or " + "contains an unescaped $ character (use $$ instead)"); void SubstituteAndAppend( std::string* output, const char* format, const substitute_internal::Arg& a0, @@ -461,9 +506,11 @@ void SubstituteAndAppend( const substitute_internal::Arg& a5, const substitute_internal::Arg& a6, const substitute_internal::Arg& a7, const substitute_internal::Arg& a8, const substitute_internal::Arg& a9) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 1023, - "There were 10 substitution arguments given, but this " - "format string doesn't contain all of $0 through $9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 1023, + "There were 10 substitution arguments given, but this " + "format string either doesn't contain all of $0 through $9 or " + "contains an unescaped $ character (use $$ instead)"); #endif // ABSL_BAD_CALL_IF // Substitute() @@ -589,47 +636,53 @@ ABSL_MUST_USE_RESULT inline std::string Substitute( std::string Substitute(const char* format) ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 0, "There were no substitution arguments " - "but this format string has a $[0-9] in it"); + "but this format string either has a $[0-9] in it or " + "contains an unescaped $ character (use $$ instead)"); std::string Substitute(const char* format, const substitute_internal::Arg& a0) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 1, - "There was 1 substitution argument given, but " - "this format string is either missing its $0, or " - "contains one of $1-$9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 1, + "There was 1 substitution argument given, but " + "this format string is missing its $0, contains one of $1-$9, " + "or contains an unescaped $ character (use $$ instead)"); std::string Substitute(const char* format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a1) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 3, - "There were 2 substitution arguments given, but " - "this format string is either missing its $0/$1, or " - "contains one of $2-$9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 3, + "There were 2 substitution arguments given, but " + "this format string is missing its $0/$1, contains one of " + "$2-$9, or contains an unescaped $ character (use $$ instead)"); std::string Substitute(const char* format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, const substitute_internal::Arg& a2) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 7, - "There were 3 substitution arguments given, but " - "this format string is either missing its $0/$1/$2, or " - "contains one of $3-$9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 7, + "There were 3 substitution arguments given, but " + "this format string is missing its $0/$1/$2, contains one of " + "$3-$9, or contains an unescaped $ character (use $$ instead)"); std::string Substitute(const char* format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, const substitute_internal::Arg& a2, const substitute_internal::Arg& a3) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 15, - "There were 4 substitution arguments given, but " - "this format string is either missing its $0-$3, or " - "contains one of $4-$9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 15, + "There were 4 substitution arguments given, but " + "this format string is missing its $0-$3, contains one of " + "$4-$9, or contains an unescaped $ character (use $$ instead)"); std::string Substitute(const char* format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, const substitute_internal::Arg& a2, const substitute_internal::Arg& a3, const substitute_internal::Arg& a4) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 31, - "There were 5 substitution arguments given, but " - "this format string is either missing its $0-$4, or " - "contains one of $5-$9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 31, + "There were 5 substitution arguments given, but " + "this format string is missing its $0-$4, contains one of " + "$5-$9, or contains an unescaped $ character (use $$ instead)"); std::string Substitute(const char* format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, @@ -637,10 +690,11 @@ std::string Substitute(const char* format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a3, const substitute_internal::Arg& a4, const substitute_internal::Arg& a5) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 63, - "There were 6 substitution arguments given, but " - "this format string is either missing its $0-$5, or " - "contains one of $6-$9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 63, + "There were 6 substitution arguments given, but " + "this format string is missing its $0-$5, contains one of " + "$6-$9, or contains an unescaped $ character (use $$ instead)"); std::string Substitute(const char* format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, @@ -649,10 +703,11 @@ std::string Substitute(const char* format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a4, const substitute_internal::Arg& a5, const substitute_internal::Arg& a6) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 127, - "There were 7 substitution arguments given, but " - "this format string is either missing its $0-$6, or " - "contains one of $7-$9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 127, + "There were 7 substitution arguments given, but " + "this format string is missing its $0-$6, contains one of " + "$7-$9, or contains an unescaped $ character (use $$ instead)"); std::string Substitute(const char* format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, @@ -662,10 +717,11 @@ std::string Substitute(const char* format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a5, const substitute_internal::Arg& a6, const substitute_internal::Arg& a7) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 255, - "There were 8 substitution arguments given, but " - "this format string is either missing its $0-$7, or " - "contains one of $8-$9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 255, + "There were 8 substitution arguments given, but " + "this format string is missing its $0-$7, contains one of " + "$8-$9, or contains an unescaped $ character (use $$ instead)"); std::string Substitute( const char* format, const substitute_internal::Arg& a0, @@ -676,7 +732,8 @@ std::string Substitute( ABSL_BAD_CALL_IF( substitute_internal::PlaceholderBitmask(format) != 511, "There were 9 substitution arguments given, but " - "this format string is either missing its $0-$8, or contains a $9"); + "this format string is missing its $0-$8, contains a $9, or " + "contains an unescaped $ character (use $$ instead)"); std::string Substitute( const char* format, const substitute_internal::Arg& a0, @@ -685,9 +742,11 @@ std::string Substitute( const substitute_internal::Arg& a5, const substitute_internal::Arg& a6, const substitute_internal::Arg& a7, const substitute_internal::Arg& a8, const substitute_internal::Arg& a9) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 1023, - "There were 10 substitution arguments given, but this " - "format string doesn't contain all of $0 through $9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 1023, + "There were 10 substitution arguments given, but this " + "format string either doesn't contain all of $0 through $9 or " + "contains an unescaped $ character (use $$ instead)"); #endif // ABSL_BAD_CALL_IF ABSL_NAMESPACE_END diff --git a/Siv3D/include/ThirdParty/absl/types/any.h b/Siv3D/include/ThirdParty/absl/types/any.h index fc5a07469f..61f071f19b 100644 --- a/Siv3D/include/ThirdParty/absl/types/any.h +++ b/Siv3D/include/ThirdParty/absl/types/any.h @@ -53,6 +53,7 @@ #ifndef ABSL_TYPES_ANY_H_ #define ABSL_TYPES_ANY_H_ +#include "absl/base/attributes.h" #include "absl/base/config.h" #include "absl/utility/utility.h" @@ -81,18 +82,9 @@ ABSL_NAMESPACE_END #include #include "absl/base/internal/fast_type_id.h" -#include "absl/base/macros.h" #include "absl/meta/type_traits.h" #include "absl/types/bad_any_cast.h" -// NOTE: This macro is an implementation detail that is undefined at the bottom -// of the file. It is not intended for expansion directly from user code. -#ifdef ABSL_ANY_DETAIL_HAS_RTTI -#error ABSL_ANY_DETAIL_HAS_RTTI cannot be directly set -#elif !defined(__GNUC__) || defined(__GXX_RTTI) -#define ABSL_ANY_DETAIL_HAS_RTTI 1 -#endif // !defined(__GNUC__) || defined(__GXX_RTTI) - namespace absl { ABSL_NAMESPACE_BEGIN @@ -297,7 +289,7 @@ class any { typename T, typename... Args, typename VT = absl::decay_t, absl::enable_if_t::value && std::is_constructible::value>* = nullptr> - VT& emplace(Args&&... args) { + VT& emplace(Args&&... args) ABSL_ATTRIBUTE_LIFETIME_BOUND { reset(); // NOTE: reset() is required here even in the world of exceptions. Obj* const object_ptr = new Obj(in_place, std::forward(args)...); @@ -321,7 +313,8 @@ class any { absl::enable_if_t::value && std::is_constructible&, Args...>::value>* = nullptr> - VT& emplace(std::initializer_list ilist, Args&&... args) { + VT& emplace(std::initializer_list ilist, + Args&&... args) ABSL_ATTRIBUTE_LIFETIME_BOUND { reset(); // NOTE: reset() is required here even in the world of exceptions. Obj* const object_ptr = new Obj(in_place, ilist, std::forward(args)...); @@ -348,7 +341,7 @@ class any { // returns `false`. bool has_value() const noexcept { return obj_ != nullptr; } -#if ABSL_ANY_DETAIL_HAS_RTTI +#ifdef ABSL_INTERNAL_HAS_RTTI // Returns: typeid(T) if *this has a contained object of type T, otherwise // typeid(void). const std::type_info& type() const noexcept { @@ -358,7 +351,7 @@ class any { return typeid(void); } -#endif // ABSL_ANY_DETAIL_HAS_RTTI +#endif // ABSL_INTERNAL_HAS_RTTI private: // Tagged type-erased abstraction for holding a cloneable object. @@ -367,9 +360,9 @@ class any { virtual ~ObjInterface() = default; virtual std::unique_ptr Clone() const = 0; virtual const void* ObjTypeId() const noexcept = 0; -#if ABSL_ANY_DETAIL_HAS_RTTI +#ifdef ABSL_INTERNAL_HAS_RTTI virtual const std::type_info& Type() const noexcept = 0; -#endif // ABSL_ANY_DETAIL_HAS_RTTI +#endif // ABSL_INTERNAL_HAS_RTTI }; // Hold a value of some queryable type, with an ability to Clone it. @@ -386,9 +379,9 @@ class any { const void* ObjTypeId() const noexcept final { return IdForType(); } -#if ABSL_ANY_DETAIL_HAS_RTTI +#ifdef ABSL_INTERNAL_HAS_RTTI const std::type_info& Type() const noexcept final { return typeid(T); } -#endif // ABSL_ANY_DETAIL_HAS_RTTI +#endif // ABSL_INTERNAL_HAS_RTTI T value; }; @@ -521,8 +514,6 @@ T* any_cast(any* operand) noexcept { ABSL_NAMESPACE_END } // namespace absl -#undef ABSL_ANY_DETAIL_HAS_RTTI - #endif // ABSL_USES_STD_ANY #endif // ABSL_TYPES_ANY_H_ diff --git a/Siv3D/include/ThirdParty/absl/types/bad_optional_access.h b/Siv3D/include/ThirdParty/absl/types/bad_optional_access.h index a500286adc..049e72ad9a 100644 --- a/Siv3D/include/ThirdParty/absl/types/bad_optional_access.h +++ b/Siv3D/include/ThirdParty/absl/types/bad_optional_access.h @@ -67,7 +67,7 @@ class bad_optional_access : public std::exception { namespace optional_internal { // throw delegator -[[noreturn]] void throw_bad_optional_access(); +[[noreturn]] ABSL_DLL void throw_bad_optional_access(); } // namespace optional_internal ABSL_NAMESPACE_END diff --git a/Siv3D/include/ThirdParty/absl/types/bad_variant_access.h b/Siv3D/include/ThirdParty/absl/types/bad_variant_access.h index 095969f91e..8ab215e97d 100644 --- a/Siv3D/include/ThirdParty/absl/types/bad_variant_access.h +++ b/Siv3D/include/ThirdParty/absl/types/bad_variant_access.h @@ -70,8 +70,8 @@ class bad_variant_access : public std::exception { namespace variant_internal { -[[noreturn]] void ThrowBadVariantAccess(); -[[noreturn]] void Rethrow(); +[[noreturn]] ABSL_DLL void ThrowBadVariantAccess(); +[[noreturn]] ABSL_DLL void Rethrow(); } // namespace variant_internal ABSL_NAMESPACE_END diff --git a/Siv3D/include/ThirdParty/absl/types/compare.h b/Siv3D/include/ThirdParty/absl/types/compare.h index 19b076e7f1..2b89b69c29 100644 --- a/Siv3D/include/ThirdParty/absl/types/compare.h +++ b/Siv3D/include/ThirdParty/absl/types/compare.h @@ -36,6 +36,7 @@ #include #include "absl/base/attributes.h" +#include "absl/base/macros.h" #include "absl/meta/type_traits.h" namespace absl { @@ -44,29 +45,37 @@ namespace compare_internal { using value_type = int8_t; -template -struct Fail { - static_assert(sizeof(T) < 0, "Only literal `0` is allowed."); -}; - -// We need the NullPtrT template to avoid triggering the modernize-use-nullptr -// ClangTidy warning in user code. -template -struct OnlyLiteralZero { - constexpr OnlyLiteralZero(NullPtrT) noexcept {} // NOLINT +class OnlyLiteralZero { + public: +#if ABSL_HAVE_ATTRIBUTE(enable_if) + // On clang, we can avoid triggering modernize-use-nullptr by only enabling + // this overload when the value is a compile time integer constant equal to 0. + // + // In c++20, this could be a static_assert in a consteval function. + constexpr OnlyLiteralZero(int n) // NOLINT + __attribute__((enable_if(n == 0, "Only literal `0` is allowed."))) {} +#else // ABSL_HAVE_ATTRIBUTE(enable_if) + // Accept only literal zero since it can be implicitly converted to a pointer + // to member type. nullptr constants will be caught by the other constructor + // which accepts a nullptr_t. + // + // This constructor is not used for clang since it triggers + // modernize-use-nullptr. + constexpr OnlyLiteralZero(int OnlyLiteralZero::*) noexcept {} // NOLINT +#endif // Fails compilation when `nullptr` or integral type arguments other than // `int` are passed. This constructor doesn't accept `int` because literal `0` // has type `int`. Literal `0` arguments will be implicitly converted to // `std::nullptr_t` and accepted by the above constructor, while other `int` // arguments will fail to be converted and cause compilation failure. - template < - typename T, - typename = typename std::enable_if< - std::is_same::value || - (std::is_integral::value && !std::is_same::value)>::type, - typename = typename Fail::type> - OnlyLiteralZero(T); // NOLINT + template ::value || + (std::is_integral::value && + !std::is_same::value)>::type> + OnlyLiteralZero(T) { // NOLINT + static_assert(sizeof(T) < 0, "Only literal `0` is allowed."); + } }; enum class eq : value_type { @@ -163,18 +172,18 @@ class weak_equality // Comparisons friend constexpr bool operator==( - weak_equality v, compare_internal::OnlyLiteralZero<>) noexcept { + weak_equality v, compare_internal::OnlyLiteralZero) noexcept { return v.value_ == 0; } friend constexpr bool operator!=( - weak_equality v, compare_internal::OnlyLiteralZero<>) noexcept { + weak_equality v, compare_internal::OnlyLiteralZero) noexcept { return v.value_ != 0; } - friend constexpr bool operator==(compare_internal::OnlyLiteralZero<>, + friend constexpr bool operator==(compare_internal::OnlyLiteralZero, weak_equality v) noexcept { return 0 == v.value_; } - friend constexpr bool operator!=(compare_internal::OnlyLiteralZero<>, + friend constexpr bool operator!=(compare_internal::OnlyLiteralZero, weak_equality v) noexcept { return 0 != v.value_; } @@ -214,18 +223,18 @@ class strong_equality } // Comparisons friend constexpr bool operator==( - strong_equality v, compare_internal::OnlyLiteralZero<>) noexcept { + strong_equality v, compare_internal::OnlyLiteralZero) noexcept { return v.value_ == 0; } friend constexpr bool operator!=( - strong_equality v, compare_internal::OnlyLiteralZero<>) noexcept { + strong_equality v, compare_internal::OnlyLiteralZero) noexcept { return v.value_ != 0; } - friend constexpr bool operator==(compare_internal::OnlyLiteralZero<>, + friend constexpr bool operator==(compare_internal::OnlyLiteralZero, strong_equality v) noexcept { return 0 == v.value_; } - friend constexpr bool operator!=(compare_internal::OnlyLiteralZero<>, + friend constexpr bool operator!=(compare_internal::OnlyLiteralZero, strong_equality v) noexcept { return 0 != v.value_; } @@ -277,50 +286,50 @@ class partial_ordering } // Comparisons friend constexpr bool operator==( - partial_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + partial_ordering v, compare_internal::OnlyLiteralZero) noexcept { return v.is_ordered() && v.value_ == 0; } friend constexpr bool operator!=( - partial_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + partial_ordering v, compare_internal::OnlyLiteralZero) noexcept { return !v.is_ordered() || v.value_ != 0; } friend constexpr bool operator<( - partial_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + partial_ordering v, compare_internal::OnlyLiteralZero) noexcept { return v.is_ordered() && v.value_ < 0; } friend constexpr bool operator<=( - partial_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + partial_ordering v, compare_internal::OnlyLiteralZero) noexcept { return v.is_ordered() && v.value_ <= 0; } friend constexpr bool operator>( - partial_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + partial_ordering v, compare_internal::OnlyLiteralZero) noexcept { return v.is_ordered() && v.value_ > 0; } friend constexpr bool operator>=( - partial_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + partial_ordering v, compare_internal::OnlyLiteralZero) noexcept { return v.is_ordered() && v.value_ >= 0; } - friend constexpr bool operator==(compare_internal::OnlyLiteralZero<>, + friend constexpr bool operator==(compare_internal::OnlyLiteralZero, partial_ordering v) noexcept { return v.is_ordered() && 0 == v.value_; } - friend constexpr bool operator!=(compare_internal::OnlyLiteralZero<>, + friend constexpr bool operator!=(compare_internal::OnlyLiteralZero, partial_ordering v) noexcept { return !v.is_ordered() || 0 != v.value_; } - friend constexpr bool operator<(compare_internal::OnlyLiteralZero<>, + friend constexpr bool operator<(compare_internal::OnlyLiteralZero, partial_ordering v) noexcept { return v.is_ordered() && 0 < v.value_; } - friend constexpr bool operator<=(compare_internal::OnlyLiteralZero<>, + friend constexpr bool operator<=(compare_internal::OnlyLiteralZero, partial_ordering v) noexcept { return v.is_ordered() && 0 <= v.value_; } - friend constexpr bool operator>(compare_internal::OnlyLiteralZero<>, + friend constexpr bool operator>(compare_internal::OnlyLiteralZero, partial_ordering v) noexcept { return v.is_ordered() && 0 > v.value_; } - friend constexpr bool operator>=(compare_internal::OnlyLiteralZero<>, + friend constexpr bool operator>=(compare_internal::OnlyLiteralZero, partial_ordering v) noexcept { return v.is_ordered() && 0 >= v.value_; } @@ -369,50 +378,50 @@ class weak_ordering } // Comparisons friend constexpr bool operator==( - weak_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + weak_ordering v, compare_internal::OnlyLiteralZero) noexcept { return v.value_ == 0; } friend constexpr bool operator!=( - weak_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + weak_ordering v, compare_internal::OnlyLiteralZero) noexcept { return v.value_ != 0; } friend constexpr bool operator<( - weak_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + weak_ordering v, compare_internal::OnlyLiteralZero) noexcept { return v.value_ < 0; } friend constexpr bool operator<=( - weak_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + weak_ordering v, compare_internal::OnlyLiteralZero) noexcept { return v.value_ <= 0; } friend constexpr bool operator>( - weak_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + weak_ordering v, compare_internal::OnlyLiteralZero) noexcept { return v.value_ > 0; } friend constexpr bool operator>=( - weak_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + weak_ordering v, compare_internal::OnlyLiteralZero) noexcept { return v.value_ >= 0; } - friend constexpr bool operator==(compare_internal::OnlyLiteralZero<>, + friend constexpr bool operator==(compare_internal::OnlyLiteralZero, weak_ordering v) noexcept { return 0 == v.value_; } - friend constexpr bool operator!=(compare_internal::OnlyLiteralZero<>, + friend constexpr bool operator!=(compare_internal::OnlyLiteralZero, weak_ordering v) noexcept { return 0 != v.value_; } - friend constexpr bool operator<(compare_internal::OnlyLiteralZero<>, + friend constexpr bool operator<(compare_internal::OnlyLiteralZero, weak_ordering v) noexcept { return 0 < v.value_; } - friend constexpr bool operator<=(compare_internal::OnlyLiteralZero<>, + friend constexpr bool operator<=(compare_internal::OnlyLiteralZero, weak_ordering v) noexcept { return 0 <= v.value_; } - friend constexpr bool operator>(compare_internal::OnlyLiteralZero<>, + friend constexpr bool operator>(compare_internal::OnlyLiteralZero, weak_ordering v) noexcept { return 0 > v.value_; } - friend constexpr bool operator>=(compare_internal::OnlyLiteralZero<>, + friend constexpr bool operator>=(compare_internal::OnlyLiteralZero, weak_ordering v) noexcept { return 0 >= v.value_; } @@ -468,50 +477,50 @@ class strong_ordering } // Comparisons friend constexpr bool operator==( - strong_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + strong_ordering v, compare_internal::OnlyLiteralZero) noexcept { return v.value_ == 0; } friend constexpr bool operator!=( - strong_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + strong_ordering v, compare_internal::OnlyLiteralZero) noexcept { return v.value_ != 0; } friend constexpr bool operator<( - strong_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + strong_ordering v, compare_internal::OnlyLiteralZero) noexcept { return v.value_ < 0; } friend constexpr bool operator<=( - strong_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + strong_ordering v, compare_internal::OnlyLiteralZero) noexcept { return v.value_ <= 0; } friend constexpr bool operator>( - strong_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + strong_ordering v, compare_internal::OnlyLiteralZero) noexcept { return v.value_ > 0; } friend constexpr bool operator>=( - strong_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + strong_ordering v, compare_internal::OnlyLiteralZero) noexcept { return v.value_ >= 0; } - friend constexpr bool operator==(compare_internal::OnlyLiteralZero<>, + friend constexpr bool operator==(compare_internal::OnlyLiteralZero, strong_ordering v) noexcept { return 0 == v.value_; } - friend constexpr bool operator!=(compare_internal::OnlyLiteralZero<>, + friend constexpr bool operator!=(compare_internal::OnlyLiteralZero, strong_ordering v) noexcept { return 0 != v.value_; } - friend constexpr bool operator<(compare_internal::OnlyLiteralZero<>, + friend constexpr bool operator<(compare_internal::OnlyLiteralZero, strong_ordering v) noexcept { return 0 < v.value_; } - friend constexpr bool operator<=(compare_internal::OnlyLiteralZero<>, + friend constexpr bool operator<=(compare_internal::OnlyLiteralZero, strong_ordering v) noexcept { return 0 <= v.value_; } - friend constexpr bool operator>(compare_internal::OnlyLiteralZero<>, + friend constexpr bool operator>(compare_internal::OnlyLiteralZero, strong_ordering v) noexcept { return 0 > v.value_; } - friend constexpr bool operator>=(compare_internal::OnlyLiteralZero<>, + friend constexpr bool operator>=(compare_internal::OnlyLiteralZero, strong_ordering v) noexcept { return 0 >= v.value_; } @@ -544,9 +553,9 @@ namespace compare_internal { // Helper functions to do a boolean comparison of two keys given a boolean // or three-way comparator. // SFINAE prevents implicit conversions to bool (such as from int). -template ::value, int> = 0> -constexpr bool compare_result_as_less_than(const Bool r) { return r; } +template ::value, int> = 0> +constexpr bool compare_result_as_less_than(const BoolT r) { return r; } constexpr bool compare_result_as_less_than(const absl::weak_ordering r) { return r < 0; } diff --git a/Siv3D/include/ThirdParty/absl/types/internal/conformance_profile.h b/Siv3D/include/ThirdParty/absl/types/internal/conformance_profile.h index cf64ff4fcd..37b017db47 100644 --- a/Siv3D/include/ThirdParty/absl/types/internal/conformance_profile.h +++ b/Siv3D/include/ThirdParty/absl/types/internal/conformance_profile.h @@ -719,6 +719,7 @@ struct SyntacticConformanceProfileOf { type##_support); \ ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF_IMPL(bool, is_##type) +#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(default_constructible); ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(move_constructible); ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(copy_constructible); @@ -733,6 +734,7 @@ ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(greater_equal_comparable); ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(greater_than_comparable); ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(swappable); ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(hashable); +#endif #undef ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF #undef ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF_IMPL diff --git a/Siv3D/include/ThirdParty/absl/types/internal/conformance_testing.h b/Siv3D/include/ThirdParty/absl/types/internal/conformance_testing.h new file mode 100644 index 0000000000..487b0f786b --- /dev/null +++ b/Siv3D/include/ThirdParty/absl/types/internal/conformance_testing.h @@ -0,0 +1,1386 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// conformance_testing.h +// ----------------------------------------------------------------------------- +// + +#ifndef ABSL_TYPES_INTERNAL_CONFORMANCE_TESTING_H_ +#define ABSL_TYPES_INTERNAL_CONFORMANCE_TESTING_H_ + +//////////////////////////////////////////////////////////////////////////////// +// // +// Many templates in this file take a `T` and a `Prof` type as explicit // +// template arguments. These are a type to be checked and a // +// "Regularity Profile" that describes what operations that type `T` is // +// expected to support. See "regularity_profiles.h" for more details // +// regarding Regularity Profiles. // +// // +//////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "absl/meta/type_traits.h" +#include "absl/strings/ascii.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "absl/types/internal/conformance_aliases.h" +#include "absl/types/internal/conformance_archetype.h" +#include "absl/types/internal/conformance_profile.h" +#include "absl/types/internal/conformance_testing_helpers.h" +#include "absl/types/internal/parentheses.h" +#include "absl/types/internal/transform_args.h" +#include "absl/utility/utility.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace types_internal { + +// Returns true if the compiler incorrectly greedily instantiates constexpr +// templates in any unevaluated context. +constexpr bool constexpr_instantiation_when_unevaluated() { +#if defined(__apple_build_version__) // TODO(calabrese) Make more specific + return true; +#elif defined(__clang__) + return __clang_major__ < 4; +#elif defined(__GNUC__) + // TODO(calabrese) Figure out why gcc 7 fails (seems like a different bug) + return __GNUC__ < 5 || (__GNUC__ == 5 && __GNUC_MINOR__ < 2) || __GNUC__ >= 7; +#else + return false; +#endif +} + +// Returns true if the standard library being used incorrectly produces an error +// when instantiating the definition of a poisoned std::hash specialization. +constexpr bool poisoned_hash_fails_instantiation() { +#if defined(_MSC_VER) && !defined(_LIBCPP_VERSION) + return _MSC_VER < 1914; +#else + return false; +#endif +} + +template +struct GeneratorType { + decltype(std::declval()()) operator()() const + noexcept(noexcept(std::declval()())) { + return fun(); + } + + Fun fun; + const char* description; +}; + +// A "make" function for the GeneratorType template that deduces the function +// object type. +template ::value>** = nullptr> +GeneratorType Generator(Fun fun, const char* description) { + return GeneratorType{absl::move(fun), description}; +} + +// A type that contains a set of nullary function objects that each return an +// instance of the same type and value (though possibly different +// representations, such as +0 and -0 or two vectors with the same elements but +// with different capacities). +template +struct EquivalenceClassType { + std::tuple...> generators; +}; + +// A "make" function for the EquivalenceClassType template that deduces the +// function object types and is constrained such that a user can only pass in +// function objects that all have the same return type. +template ::value>** = nullptr> +EquivalenceClassType EquivalenceClass(GeneratorType... funs) { + return {std::make_tuple(absl::move(funs)...)}; +} + +// A type that contains an ordered series of EquivalenceClassTypes, from +// smallest value to largest value. +template +struct OrderedEquivalenceClasses { + std::tuple eq_classes; +}; + +// An object containing the parts of a given (name, initialization expression), +// and is capable of generating a string that describes the given. +struct GivenDeclaration { + std::string outputDeclaration(std::size_t width) const { + const std::size_t indent_size = 2; + std::string result = absl::StrCat(" ", name); + + if (!expression.empty()) { + // Indent + result.resize(indent_size + width, ' '); + absl::StrAppend(&result, " = ", expression, ";\n"); + } else { + absl::StrAppend(&result, ";\n"); + } + + return result; + } + + std::string name; + std::string expression; +}; + +// Produce a string that contains all of the givens of an error report. +template +std::string PrepareGivenContext(const Decls&... decls) { + const std::size_t width = (std::max)({decls.name.size()...}); + return absl::StrCat("Given:\n", decls.outputDeclaration(width)..., "\n"); +} + +//////////////////////////////////////////////////////////////////////////////// +// Function objects that perform a check for each comparison operator // +//////////////////////////////////////////////////////////////////////////////// + +#define ABSL_INTERNAL_EXPECT_OP(name, op) \ + struct Expect##name { \ + template \ + void operator()(absl::string_view test_name, absl::string_view context, \ + const T& lhs, const T& rhs, absl::string_view lhs_name, \ + absl::string_view rhs_name) const { \ + if (!static_cast(lhs op rhs)) { \ + errors->addTestFailure( \ + test_name, absl::StrCat(context, \ + "**Unexpected comparison result**\n" \ + "\n" \ + "Expression:\n" \ + " ", \ + lhs_name, " " #op " ", rhs_name, \ + "\n" \ + "\n" \ + "Expected: true\n" \ + " Actual: false")); \ + } else { \ + errors->addTestSuccess(test_name); \ + } \ + } \ + \ + ConformanceErrors* errors; \ + }; \ + \ + struct ExpectNot##name { \ + template \ + void operator()(absl::string_view test_name, absl::string_view context, \ + const T& lhs, const T& rhs, absl::string_view lhs_name, \ + absl::string_view rhs_name) const { \ + if (lhs op rhs) { \ + errors->addTestFailure( \ + test_name, absl::StrCat(context, \ + "**Unexpected comparison result**\n" \ + "\n" \ + "Expression:\n" \ + " ", \ + lhs_name, " " #op " ", rhs_name, \ + "\n" \ + "\n" \ + "Expected: false\n" \ + " Actual: true")); \ + } else { \ + errors->addTestSuccess(test_name); \ + } \ + } \ + \ + ConformanceErrors* errors; \ + } + +ABSL_INTERNAL_EXPECT_OP(Eq, ==); +ABSL_INTERNAL_EXPECT_OP(Ne, !=); +ABSL_INTERNAL_EXPECT_OP(Lt, <); +ABSL_INTERNAL_EXPECT_OP(Le, <=); +ABSL_INTERNAL_EXPECT_OP(Ge, >=); +ABSL_INTERNAL_EXPECT_OP(Gt, >); + +#undef ABSL_INTERNAL_EXPECT_OP + +// A function object that verifies that two objects hash to the same value by +// way of the std::hash specialization. +struct ExpectSameHash { + template + void operator()(absl::string_view test_name, absl::string_view context, + const T& lhs, const T& rhs, absl::string_view lhs_name, + absl::string_view rhs_name) const { + if (std::hash()(lhs) != std::hash()(rhs)) { + errors->addTestFailure( + test_name, absl::StrCat(context, + "**Unexpected hash result**\n" + "\n" + "Expression:\n" + " std::hash()(", + lhs_name, ") == std::hash()(", rhs_name, + ")\n" + "\n" + "Expected: true\n" + " Actual: false")); + } else { + errors->addTestSuccess(test_name); + } + } + + ConformanceErrors* errors; +}; + +// A function template that takes two objects and verifies that each comparison +// operator behaves in a way that is consistent with equality. It has "OneWay" +// in the name because the first argument will always be the left-hand operand +// of the corresponding comparison operator and the second argument will +// always be the right-hand operand. It will never switch that order. +// At a higher level in the test suite, the one-way form is called once for each +// of the two possible orders whenever lhs and rhs are not the same initializer. +template +void ExpectOneWayEquality(ConformanceErrors* errors, + absl::string_view test_name, + absl::string_view context, const T& lhs, const T& rhs, + absl::string_view lhs_name, + absl::string_view rhs_name) { + If::is_equality_comparable>::Invoke( + ExpectEq{errors}, test_name, context, lhs, rhs, lhs_name, rhs_name); + + If::is_inequality_comparable>::Invoke( + ExpectNotNe{errors}, test_name, context, lhs, rhs, lhs_name, rhs_name); + + If::is_less_than_comparable>::Invoke( + ExpectNotLt{errors}, test_name, context, lhs, rhs, lhs_name, rhs_name); + + If::is_less_equal_comparable>::Invoke( + ExpectLe{errors}, test_name, context, lhs, rhs, lhs_name, rhs_name); + + If::is_greater_equal_comparable>::Invoke( + ExpectGe{errors}, test_name, context, lhs, rhs, lhs_name, rhs_name); + + If::is_greater_than_comparable>::Invoke( + ExpectNotGt{errors}, test_name, context, lhs, rhs, lhs_name, rhs_name); + + If::is_hashable>::Invoke( + ExpectSameHash{errors}, test_name, context, lhs, rhs, lhs_name, rhs_name); +} + +// A function template that takes two objects and verifies that each comparison +// operator behaves in a way that is consistent with equality. This function +// differs from ExpectOneWayEquality in that this will do checks with argument +// order reversed in addition to in-order. +template +void ExpectEquality(ConformanceErrors* errors, absl::string_view test_name, + absl::string_view context, const T& lhs, const T& rhs, + absl::string_view lhs_name, absl::string_view rhs_name) { + (ExpectOneWayEquality)(errors, test_name, context, lhs, rhs, + lhs_name, rhs_name); + (ExpectOneWayEquality)(errors, test_name, context, rhs, lhs, + rhs_name, lhs_name); +} + +// Given a generator, makes sure that a generated value and a moved-from +// generated value are equal. +template +struct ExpectMoveConstructOneGenerator { + template + void operator()(const Fun& generator) const { + const T object = generator(); + const T moved_object = absl::move(generator()); // Force no elision. + + (ExpectEquality)(errors, "Move construction", + PrepareGivenContext( + GivenDeclaration{"const _T object", + generator.description}, + GivenDeclaration{"const _T moved_object", + std::string("std::move(") + + generator.description + + ")"}), + object, moved_object, "object", "moved_object"); + } + + ConformanceErrors* errors; +}; + +// Given a generator, makes sure that a generated value and a copied-from +// generated value are equal. +template +struct ExpectCopyConstructOneGenerator { + template + void operator()(const Fun& generator) const { + const T object = generator(); + const T copied_object = static_cast(generator()); + + (ExpectEquality)(errors, "Copy construction", + PrepareGivenContext( + GivenDeclaration{"const _T object", + generator.description}, + GivenDeclaration{ + "const _T copied_object", + std::string("static_cast(") + + generator.description + ")"}), + object, copied_object, "object", "copied_object"); + } + + ConformanceErrors* errors; +}; + +// Default-construct and do nothing before destruction. +// +// This is useful in exercising the codepath of default construction followed by +// destruction, but does not explicitly test anything. An example of where this +// might fail is a default destructor that default-initializes a scalar and a +// destructor reads the value of that member. Sanitizers can catch this as long +// as our test attempts to execute such a case. +template +struct ExpectDefaultConstructWithDestruct { + void operator()() const { + // Scoped so that destructor gets called before reporting success. + { + T object; + static_cast(object); + } + + errors->addTestSuccess("Default construction"); + } + + ConformanceErrors* errors; +}; + +// Check move-assign into a default-constructed object. +template +struct ExpectDefaultConstructWithMoveAssign { + template + void operator()(const Fun& generator) const { + const T source_of_truth = generator(); + T object; + object = generator(); + + (ExpectEquality)(errors, "Move assignment", + PrepareGivenContext( + GivenDeclaration{"const _T object", + generator.description}, + GivenDeclaration{"_T object", ""}, + GivenDeclaration{"object", + generator.description}), + object, source_of_truth, "std::as_const(object)", + "source_of_truth"); + } + + ConformanceErrors* errors; +}; + +// Check copy-assign into a default-constructed object. +template +struct ExpectDefaultConstructWithCopyAssign { + template + void operator()(const Fun& generator) const { + const T source_of_truth = generator(); + T object; + object = static_cast(generator()); + + (ExpectEquality)(errors, "Copy assignment", + PrepareGivenContext( + GivenDeclaration{"const _T source_of_truth", + generator.description}, + GivenDeclaration{"_T object", ""}, + GivenDeclaration{ + "object", + std::string("static_cast(") + + generator.description + ")"}), + object, source_of_truth, "std::as_const(object)", + "source_of_truth"); + } + + ConformanceErrors* errors; +}; + +// Perform a self move-assign. +template +struct ExpectSelfMoveAssign { + template + void operator()(const Fun& generator) const { + T object = generator(); + object = absl::move(object); + + // NOTE: Self move-assign results in a valid-but-unspecified state. + + (ExpectEquality)(errors, "Move assignment", + PrepareGivenContext( + GivenDeclaration{"_T object", + generator.description}, + GivenDeclaration{"object", + "std::move(object)"}), + object, object, "object", "object"); + } + + ConformanceErrors* errors; +}; + +// Perform a self copy-assign. +template +struct ExpectSelfCopyAssign { + template + void operator()(const Fun& generator) const { + const T source_of_truth = generator(); + T object = generator(); + const T& const_object = object; + object = const_object; + + (ExpectEquality)(errors, "Copy assignment", + PrepareGivenContext( + GivenDeclaration{"const _T source_of_truth", + generator.description}, + GivenDeclaration{"_T object", + generator.description}, + GivenDeclaration{"object", + "std::as_const(object)"}), + const_object, source_of_truth, + "std::as_const(object)", "source_of_truth"); + } + + ConformanceErrors* errors; +}; + +// Perform a self-swap. +template +struct ExpectSelfSwap { + template + void operator()(const Fun& generator) const { + const T source_of_truth = generator(); + T object = generator(); + + type_traits_internal::Swap(object, object); + + std::string preliminary_info = absl::StrCat( + PrepareGivenContext( + GivenDeclaration{"const _T source_of_truth", generator.description}, + GivenDeclaration{"_T object", generator.description}), + "After performing a self-swap:\n" + " using std::swap;\n" + " swap(object, object);\n" + "\n"); + + (ExpectEquality)(errors, "Swap", std::move(preliminary_info), + object, source_of_truth, "std::as_const(object)", + "source_of_truth"); + } + + ConformanceErrors* errors; +}; + +// Perform each of the single-generator checks when necessary operations are +// supported. +template +struct ExpectSelfComparison { + template + void operator()(const Fun& generator) const { + const T object = generator(); + (ExpectOneWayEquality)(errors, "Comparison", + PrepareGivenContext(GivenDeclaration{ + "const _T object", + generator.description}), + object, object, "object", "object"); + } + + ConformanceErrors* errors; +}; + +// Perform each of the single-generator checks when necessary operations are +// supported. +template +struct ExpectConsistency { + template + void operator()(const Fun& generator) const { + If::is_move_constructible>::Invoke( + ExpectMoveConstructOneGenerator{errors}, generator); + + If::is_copy_constructible>::Invoke( + ExpectCopyConstructOneGenerator{errors}, generator); + + If::is_default_constructible && + PropertiesOfT::is_move_assignable>:: + Invoke(ExpectDefaultConstructWithMoveAssign{errors}, + generator); + + If::is_default_constructible && + PropertiesOfT::is_copy_assignable>:: + Invoke(ExpectDefaultConstructWithCopyAssign{errors}, + generator); + + If::is_move_assignable>::Invoke( + ExpectSelfMoveAssign{errors}, generator); + + If::is_copy_assignable>::Invoke( + ExpectSelfCopyAssign{errors}, generator); + + If::is_swappable>::Invoke( + ExpectSelfSwap{errors}, generator); + } + + ConformanceErrors* errors; +}; + +// Check move-assign with two different values. +template +struct ExpectMoveAssign { + template + void operator()(const Fun0& generator0, const Fun1& generator1) const { + const T source_of_truth1 = generator1(); + T object = generator0(); + object = generator1(); + + (ExpectEquality)(errors, "Move assignment", + PrepareGivenContext( + GivenDeclaration{"const _T source_of_truth1", + generator1.description}, + GivenDeclaration{"_T object", + generator0.description}, + GivenDeclaration{"object", + generator1.description}), + object, source_of_truth1, "std::as_const(object)", + "source_of_truth1"); + } + + ConformanceErrors* errors; +}; + +// Check copy-assign with two different values. +template +struct ExpectCopyAssign { + template + void operator()(const Fun0& generator0, const Fun1& generator1) const { + const T source_of_truth1 = generator1(); + T object = generator0(); + object = static_cast(generator1()); + + (ExpectEquality)(errors, "Copy assignment", + PrepareGivenContext( + GivenDeclaration{"const _T source_of_truth1", + generator1.description}, + GivenDeclaration{"_T object", + generator0.description}, + GivenDeclaration{ + "object", + std::string("static_cast(") + + generator1.description + ")"}), + object, source_of_truth1, "std::as_const(object)", + "source_of_truth1"); + } + + ConformanceErrors* errors; +}; + +// Check swap with two different values. +template +struct ExpectSwap { + template + void operator()(const Fun0& generator0, const Fun1& generator1) const { + const T source_of_truth0 = generator0(); + const T source_of_truth1 = generator1(); + T object0 = generator0(); + T object1 = generator1(); + + type_traits_internal::Swap(object0, object1); + + const std::string context = + PrepareGivenContext( + GivenDeclaration{"const _T source_of_truth0", + generator0.description}, + GivenDeclaration{"const _T source_of_truth1", + generator1.description}, + GivenDeclaration{"_T object0", generator0.description}, + GivenDeclaration{"_T object1", generator1.description}) + + "After performing a swap:\n" + " using std::swap;\n" + " swap(object0, object1);\n" + "\n"; + + (ExpectEquality)(errors, "Swap", context, object0, + source_of_truth1, "std::as_const(object0)", + "source_of_truth1"); + (ExpectEquality)(errors, "Swap", context, object1, + source_of_truth0, "std::as_const(object1)", + "source_of_truth0"); + } + + ConformanceErrors* errors; +}; + +// Validate that `generator0` and `generator1` produce values that are equal. +template +struct ExpectEquivalenceClassComparison { + template + void operator()(const Fun0& generator0, const Fun1& generator1) const { + const T object0 = generator0(); + const T object1 = generator1(); + + (ExpectEquality)(errors, "Comparison", + PrepareGivenContext( + GivenDeclaration{"const _T object0", + generator0.description}, + GivenDeclaration{"const _T object1", + generator1.description}), + object0, object1, "object0", "object1"); + } + + ConformanceErrors* errors; +}; + +// Validate that all objects in the same equivalence-class have the same value. +template +struct ExpectEquivalenceClassConsistency { + template + void operator()(const Fun0& generator0, const Fun1& generator1) const { + If::is_move_assignable>::Invoke( + ExpectMoveAssign{errors}, generator0, generator1); + + If::is_copy_assignable>::Invoke( + ExpectCopyAssign{errors}, generator0, generator1); + + If::is_swappable>::Invoke(ExpectSwap{errors}, + generator0, generator1); + } + + ConformanceErrors* errors; +}; + +// Given a "lesser" object and a "greater" object, perform every combination of +// comparison operators supported for the type, expecting consistent results. +template +void ExpectOrdered(ConformanceErrors* errors, absl::string_view context, + const T& small, const T& big, absl::string_view small_name, + absl::string_view big_name) { + const absl::string_view test_name = "Comparison"; + + If::is_equality_comparable>::Invoke( + ExpectNotEq{errors}, test_name, context, small, big, small_name, + big_name); + If::is_equality_comparable>::Invoke( + ExpectNotEq{errors}, test_name, context, big, small, big_name, + small_name); + + If::is_inequality_comparable>::Invoke( + ExpectNe{errors}, test_name, context, small, big, small_name, big_name); + If::is_inequality_comparable>::Invoke( + ExpectNe{errors}, test_name, context, big, small, big_name, small_name); + + If::is_less_than_comparable>::Invoke( + ExpectLt{errors}, test_name, context, small, big, small_name, big_name); + If::is_less_than_comparable>::Invoke( + ExpectNotLt{errors}, test_name, context, big, small, big_name, + small_name); + + If::is_less_equal_comparable>::Invoke( + ExpectLe{errors}, test_name, context, small, big, small_name, big_name); + If::is_less_equal_comparable>::Invoke( + ExpectNotLe{errors}, test_name, context, big, small, big_name, + small_name); + + If::is_greater_equal_comparable>::Invoke( + ExpectNotGe{errors}, test_name, context, small, big, small_name, + big_name); + If::is_greater_equal_comparable>::Invoke( + ExpectGe{errors}, test_name, context, big, small, big_name, small_name); + + If::is_greater_than_comparable>::Invoke( + ExpectNotGt{errors}, test_name, context, small, big, small_name, + big_name); + If::is_greater_than_comparable>::Invoke( + ExpectGt{errors}, test_name, context, big, small, big_name, small_name); +} + +// For every two elements of an equivalence class, makes sure that those two +// elements compare equal, including checks with the same argument passed as +// both operands. +template +struct ExpectEquivalenceClassComparisons { + template + void operator()(EquivalenceClassType eq_class) const { + (ForEachTupleElement)(ExpectSelfComparison{errors}, + eq_class.generators); + + (ForEveryTwo)(ExpectEquivalenceClassComparison{errors}, + eq_class.generators); + } + + ConformanceErrors* errors; +}; + +// For every element of an equivalence class, makes sure that the element is +// self-consistent (in other words, if any of move/copy/swap are defined, +// perform those operations and make such that results and operands still +// compare equal to known values whenever it is required for that operation. +template +struct ExpectEquivalenceClass { + template + void operator()(EquivalenceClassType eq_class) const { + (ForEachTupleElement)(ExpectConsistency{errors}, + eq_class.generators); + + (ForEveryTwo)(ExpectEquivalenceClassConsistency{errors}, + eq_class.generators); + } + + ConformanceErrors* errors; +}; + +// Validate that the passed-in argument is a generator of a greater value than +// the one produced by the "small_gen" datamember with respect to all of the +// comparison operators that Prof requires, with both argument orders to test. +template +struct ExpectBiggerGeneratorThanComparisons { + template + void operator()(BigGenerator big_gen) const { + const T small = small_gen(); + const T big = big_gen(); + + (ExpectOrdered)(errors, + PrepareGivenContext( + GivenDeclaration{"const _T small", + small_gen.description}, + GivenDeclaration{"const _T big", + big_gen.description}), + small, big, "small", "big"); + } + + SmallGenerator small_gen; + ConformanceErrors* errors; +}; + +// Perform all of the move, copy, and swap checks on the value generated by +// `small_gen` and the value generated by `big_gen`. +template +struct ExpectBiggerGeneratorThan { + template + void operator()(BigGenerator big_gen) const { + If::is_move_assignable>::Invoke( + ExpectMoveAssign{errors}, small_gen, big_gen); + If::is_move_assignable>::Invoke( + ExpectMoveAssign{errors}, big_gen, small_gen); + + If::is_copy_assignable>::Invoke( + ExpectCopyAssign{errors}, small_gen, big_gen); + If::is_copy_assignable>::Invoke( + ExpectCopyAssign{errors}, big_gen, small_gen); + + If::is_swappable>::Invoke(ExpectSwap{errors}, + small_gen, big_gen); + } + + SmallGenerator small_gen; + ConformanceErrors* errors; +}; + +// Validate that the result of a generator is greater than the results of all +// generators in an equivalence class with respect to comparisons. +template +struct ExpectBiggerGeneratorThanEqClassesComparisons { + template + void operator()(BigEqClass big_eq_class) const { + (ForEachTupleElement)( + ExpectBiggerGeneratorThanComparisons{small_gen, + errors}, + big_eq_class.generators); + } + + SmallGenerator small_gen; + ConformanceErrors* errors; +}; + +// Validate that the non-comparison binary operations required by Prof are +// correct for the result of each generator of big_eq_class and a generator of +// the logically smaller value returned by small_gen. +template +struct ExpectBiggerGeneratorThanEqClasses { + template + void operator()(BigEqClass big_eq_class) const { + (ForEachTupleElement)( + ExpectBiggerGeneratorThan{small_gen, errors}, + big_eq_class.generators); + } + + SmallGenerator small_gen; + ConformanceErrors* errors; +}; + +// Validate that each equivalence class that is passed is logically less than +// the equivalence classes that comes later on in the argument list. +template +struct ExpectOrderedEquivalenceClassesComparisons { + template + struct Impl { + // Validate that the value produced by `small_gen` is less than all of the + // values generated by those of the logically larger equivalence classes. + template + void operator()(SmallGenerator small_gen) const { + (ForEachTupleElement)(ExpectBiggerGeneratorThanEqClassesComparisons< + T, Prof, SmallGenerator>{small_gen, errors}, + big_eq_classes); + } + + std::tuple big_eq_classes; + ConformanceErrors* errors; + }; + + // When given no equivalence classes, no validation is necessary. + void operator()() const {} + + template + void operator()(SmallEqClass small_eq_class, + BigEqClasses... big_eq_classes) const { + // For each generator in the first equivalence class, make sure that it is + // less than each of those in the logically greater equivalence classes. + (ForEachTupleElement)( + Impl{std::make_tuple(absl::move(big_eq_classes)...), + errors}, + small_eq_class.generators); + + // Recurse so that all equivalence class combinations are checked. + (*this)(absl::move(big_eq_classes)...); + } + + ConformanceErrors* errors; +}; + +// Validate that the non-comparison binary operations required by Prof are +// correct for the result of each generator of big_eq_classes and a generator of +// the logically smaller value returned by small_gen. +template +struct ExpectOrderedEquivalenceClasses { + template + struct Impl { + template + void operator()(SmallGenerator small_gen) const { + (ForEachTupleElement)( + ExpectBiggerGeneratorThanEqClasses{small_gen, + errors}, + big_eq_classes); + } + + std::tuple big_eq_classes; + ConformanceErrors* errors; + }; + + // Check that small_eq_class is logically consistent and also is logically + // less than all values in big_eq_classes. + template + void operator()(SmallEqClass small_eq_class, + BigEqClasses... big_eq_classes) const { + (ForEachTupleElement)( + Impl{std::make_tuple(absl::move(big_eq_classes)...), + errors}, + small_eq_class.generators); + + (*this)(absl::move(big_eq_classes)...); + } + + // Terminating case of operator(). + void operator()() const {} + + ConformanceErrors* errors; +}; + +// Validate that a type meets the syntactic requirements of std::hash if the +// range of profiles requires it. +template +struct ExpectHashable { + void operator()() const { + ExpectModelOfHashable(errors); + } + + ConformanceErrors* errors; +}; + +// Validate that the type `T` meets all of the requirements associated with +// `MinProf` and without going beyond the syntactic properties of `MaxProf`. +template +struct ExpectModels { + void operator()(ConformanceErrors* errors) const { + ExpectModelOfDefaultConstructible(errors); + ExpectModelOfMoveConstructible(errors); + ExpectModelOfCopyConstructible(errors); + ExpectModelOfMoveAssignable(errors); + ExpectModelOfCopyAssignable(errors); + ExpectModelOfDestructible(errors); + ExpectModelOfEqualityComparable(errors); + ExpectModelOfInequalityComparable(errors); + ExpectModelOfLessThanComparable(errors); + ExpectModelOfLessEqualComparable(errors); + ExpectModelOfGreaterEqualComparable(errors); + ExpectModelOfGreaterThanComparable(errors); + ExpectModelOfSwappable(errors); + + // Only check hashability on compilers that have a compliant default-hash. + If::Invoke( + ExpectHashable{errors}); + } +}; + +// A metafunction that yields a Profile matching the set of properties that are +// safe to be checked (lack-of-hashability is only checked on standard library +// implementations that are standards compliant in that they provide a std::hash +// primary template that is SFINAE-friendly) +template +struct MinimalCheckableProfile { + using type = + MinimalProfiles, + PropertiesOfT::is_hashable && + poisoned_hash_fails_instantiation() + ? CheckHashability::no + : CheckHashability::yes>>>; +}; + +// An identity metafunction +template +struct Always { + using type = T; +}; + +// Validate the T meets all of the necessary requirements of LogicalProf, with +// syntactic requirements defined by the profile range [MinProf, MaxProf]. +template +ConformanceErrors ExpectRegularityImpl( + OrderedEquivalenceClasses vals) { + ConformanceErrors errors((NameOf())); + + If::Invoke( + ExpectModels(), &errors); + + using minimal_profile = typename absl::conditional_t< + constexpr_instantiation_when_unevaluated(), Always, + MinimalCheckableProfile>::type; + + If::is_default_constructible>::Invoke( + ExpectDefaultConstructWithDestruct{&errors}); + + ////////////////////////////////////////////////////////////////////////////// + // Perform all comparison checks first, since later checks depend on their + // correctness. + // + // Check all of the comparisons for all values in the same equivalence + // class (equal with respect to comparison operators and hash the same). + (ForEachTupleElement)( + ExpectEquivalenceClassComparisons{&errors}, + vals.eq_classes); + + // Check all of the comparisons for each combination of values that are in + // different equivalence classes (not equal with respect to comparison + // operators). + absl::apply( + ExpectOrderedEquivalenceClassesComparisons{&errors}, + vals.eq_classes); + // + ////////////////////////////////////////////////////////////////////////////// + + // Perform remaining checks, relying on comparisons. + // TODO(calabrese) short circuit if any comparisons above failed. + (ForEachTupleElement)(ExpectEquivalenceClass{&errors}, + vals.eq_classes); + + absl::apply(ExpectOrderedEquivalenceClasses{&errors}, + vals.eq_classes); + + return errors; +} + +// A type that represents a range of profiles that are acceptable to be matched. +// +// `MinProf` is the minimum set of syntactic requirements that must be met. +// +// `MaxProf` is the maximum set of syntactic requirements that must be met. +// This maximum is particularly useful for certain "strictness" checking. Some +// examples for when this is useful: +// +// * Making sure that a type is move-only (rather than simply movable) +// +// * Making sure that a member function is *not* noexcept in cases where it +// cannot be noexcept, such as if a dependent datamember has certain +// operations that are not noexcept. +// +// * Making sure that a type tightly matches a spec, such as the standard. +// +// `LogicalProf` is the Profile for which run-time testing is to take place. +// +// Note: The reason for `LogicalProf` is because it is often the case, when +// dealing with templates, that a declaration of a given operation is specified, +// but whose body would fail to instantiate. Examples include the +// copy-constructor of a standard container when the element-type is move-only, +// or the comparison operators of a standard container when the element-type +// does not have the necessary comparison operations defined. The `LogicalProf` +// parameter allows us to capture the intent of what should be tested at +// run-time, even in the cases where syntactically it might otherwise appear as +// though the type undergoing testing supports more than it actually does. +template +struct ProfileRange { + using logical_profile = LogicalProf; + using min_profile = MinProf; + using max_profile = MaxProf; +}; + +// Similar to ProfileRange except that it creates a profile range that is +// coupled with a Domain and is used when testing that a type matches exactly +// the "minimum" requirements of LogicalProf. +template +struct StrictProfileRange { + // We do not yet support extension. + static_assert( + std::is_same::value, + "Currently, the only valid StrictnessDomain is RegularityDomain."); + using strictness_domain = StrictnessDomain; + using logical_profile = LogicalProf; + using min_profile = MinProf; + using max_profile = MaxProf; +}; + +//////////////////////////////////////////////////////////////////////////////// +// +// A metafunction that creates a StrictProfileRange from a Domain and either a +// Profile or ProfileRange. +template +struct MakeStrictProfileRange; + +template +struct MakeStrictProfileRange { + using type = StrictProfileRange; +}; + +template +struct MakeStrictProfileRange> { + using type = + StrictProfileRange; +}; + +template +using MakeStrictProfileRangeT = + typename MakeStrictProfileRange::type; +// +//////////////////////////////////////////////////////////////////////////////// + +// A profile in the RegularityDomain with the strongest possible requirements. +using MostStrictProfile = + CombineProfiles; + +// Forms a ProfileRange that treats the Profile as the bare minimum requirements +// of a type. +template +using LooseProfileRange = StrictProfileRange; + +template +using MakeLooseProfileRangeT = Prof; + +//////////////////////////////////////////////////////////////////////////////// +// +// The following classes implement the metafunction ProfileRangeOfT that +// takes either a Profile or ProfileRange and yields the ProfileRange to be +// used during testing. +// +template +struct ProfileRangeOfImpl; + +template +struct ProfileRangeOfImpl>> { + using type = LooseProfileRange; +}; + +template +struct ProfileRangeOf : ProfileRangeOfImpl {}; + +template +struct ProfileRangeOf< + StrictProfileRange> { + using type = + StrictProfileRange; +}; + +template +using ProfileRangeOfT = typename ProfileRangeOf::type; +// +//////////////////////////////////////////////////////////////////////////////// + +// Extract the logical profile of a range (what will be runtime tested). +template +using LogicalProfileOfT = typename ProfileRangeOfT::logical_profile; + +// Extract the minimal syntactic profile of a range (error if not at least). +template +using MinProfileOfT = typename ProfileRangeOfT::min_profile; + +// Extract the maximum syntactic profile of a range (error if more than). +template +using MaxProfileOfT = typename ProfileRangeOfT::max_profile; + +//////////////////////////////////////////////////////////////////////////////// +// +template +struct IsProfileOrProfileRange : IsProfile::type {}; + +template +struct IsProfileOrProfileRange< + StrictProfileRange> + : std::true_type {}; +// +//////////////////////////////////////////////////////////////////////////////// + +// TODO(calabrese): Consider naming the functions in this class the same as +// the macros (defined later on) so that auto-complete leads to the correct name +// and so that a user cannot accidentally call a function rather than the macro +// form. +template +struct ExpectConformanceOf { + // Add a value to be tested. Subsequent calls to this function on the same + // object must specify logically "larger" values with respect to the + // comparison operators of the type, if any. + // + // NOTE: This function should not be called directly. A stateless lambda is + // implicitly formed and passed when using the INITIALIZER macro at the bottom + // of this file. + template >, T>::value>** = nullptr> + ABSL_MUST_USE_RESULT ExpectConformanceOf> + initializer(GeneratorType fun) && { + return { + {std::tuple_cat(absl::move(ordered_vals.eq_classes), + std::make_tuple((EquivalenceClass)(absl::move(fun))))}, + std::move(expected_failed_tests)}; + } + + template ...>::value>** = + nullptr> + ABSL_MUST_USE_RESULT ExpectConformanceOf + due_to(TestNames&&... test_names) && { + (InsertEach)(&expected_failed_tests, + absl::AsciiStrToLower(absl::string_view(test_names))...); + + return {absl::move(ordered_vals), std::move(expected_failed_tests)}; + } + + template ...>::value>** = + nullptr> + ABSL_MUST_USE_RESULT ExpectConformanceOf + due_to(TestNames&&... test_names) && { + // TODO(calabrese) Instead have DUE_TO only exist via a CRTP base. + // This would produce better errors messages than the static_assert. + static_assert(!ExpectSuccess, + "DUE_TO cannot be called when conformance is expected -- did " + "you mean to use ASSERT_NONCONFORMANCE_OF?"); + } + + // Add a value to be tested. Subsequent calls to this function on the same + // object must specify logically "larger" values with respect to the + // comparison operators of the type, if any. + // + // NOTE: This function should not be called directly. A stateful lambda is + // implicitly formed and passed when using the INITIALIZER macro at the bottom + // of this file. + template >, T>::value>** = nullptr> + ABSL_MUST_USE_RESULT ExpectConformanceOf> + dont_class_directly_stateful_initializer(GeneratorType fun) && { + return { + {std::tuple_cat(absl::move(ordered_vals.eq_classes), + std::make_tuple((EquivalenceClass)(absl::move(fun))))}, + std::move(expected_failed_tests)}; + } + + // Add a set of value to be tested, where each value is equal with respect to + // the comparison operators and std::hash specialization, if defined. + template < + class... Funs, + absl::void_t>, T>::value>...>** = nullptr> + ABSL_MUST_USE_RESULT ExpectConformanceOf> + equivalence_class(GeneratorType... funs) && { + return {{std::tuple_cat( + absl::move(ordered_vals.eq_classes), + std::make_tuple((EquivalenceClass)(absl::move(funs)...)))}, + std::move(expected_failed_tests)}; + } + + // Execute the tests for the captured set of values, strictly matching a range + // of expected profiles in a given domain. + template < + class ProfRange, + absl::enable_if_t::value>** = nullptr> + ABSL_MUST_USE_RESULT ::testing::AssertionResult with_strict_profile( + ProfRange /*profile*/) { + ConformanceErrors test_result = + (ExpectRegularityImpl< + T, LogicalProfileOfT, MinProfileOfT, + MaxProfileOfT>)(absl::move(ordered_vals)); + + return ExpectSuccess ? test_result.assertionResult() + : test_result.expectFailedTests(expected_failed_tests); + } + + // Execute the tests for the captured set of values, loosely matching a range + // of expected profiles (loose in that an interface is allowed to be more + // refined that a profile suggests, such as a type having a noexcept copy + // constructor when all that is required is that the copy constructor exists). + template ::value>** = nullptr> + ABSL_MUST_USE_RESULT ::testing::AssertionResult with_loose_profile( + Prof /*profile*/) { + ConformanceErrors test_result = + (ExpectRegularityImpl< + T, Prof, Prof, + CombineProfiles>)(absl:: + move(ordered_vals)); + + return ExpectSuccess ? test_result.assertionResult() + : test_result.expectFailedTests(expected_failed_tests); + } + + OrderedEquivalenceClasses ordered_vals; + std::set expected_failed_tests; +}; + +template +using ExpectConformanceOfType = ExpectConformanceOf; + +template +using ExpectNonconformanceOfType = + ExpectConformanceOf; + +struct EquivalenceClassMaker { + // TODO(calabrese) Constrain to callable + template + static GeneratorType initializer(GeneratorType fun) { + return fun; + } +}; + +// A top-level macro that begins the builder pattern. +// +// The argument here takes the datatype to be tested. +#define ABSL_INTERNAL_ASSERT_CONFORMANCE_OF(...) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if ABSL_INTERNAL_LPAREN \ + const ::testing::AssertionResult gtest_ar = \ + ABSL_INTERNAL_LPAREN ::absl::types_internal::ExpectConformanceOfType< \ + __VA_ARGS__>() + +// Akin to ASSERT_CONFORMANCE_OF except that it expects failure and tries to +// match text. +#define ABSL_INTERNAL_ASSERT_NONCONFORMANCE_OF(...) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if ABSL_INTERNAL_LPAREN \ + const ::testing::AssertionResult gtest_ar = \ + ABSL_INTERNAL_LPAREN ::absl::types_internal::ExpectNonconformanceOfType< \ + __VA_ARGS__>() + +//////////////////////////////////////////////////////////////////////////////// +// NOTE: The following macros look like they are recursive, but are not (macros +// cannot recurse). These actually refer to member functions of the same name. +// This is done intentionally so that a user cannot accidentally invoke a +// member function of the conformance-testing suite without going through the +// macro. +//////////////////////////////////////////////////////////////////////////////// + +// Specify expected test failures as comma-separated strings. +#define DUE_TO(...) due_to(__VA_ARGS__) + +// Specify a value to be tested. +// +// Note: Internally, this takes an expression and turns it into the return value +// of lambda that captures no data. The expression is stringized during +// preprocessing so that it can be used in error reports. +#define INITIALIZER(...) \ + initializer(::absl::types_internal::Generator( \ + [] { return __VA_ARGS__; }, ABSL_INTERNAL_STRINGIZE(__VA_ARGS__))) + +// Specify a value to be tested. +// +// Note: Internally, this takes an expression and turns it into the return value +// of lambda that captures data by reference. The expression is stringized +// during preprocessing so that it can be used in error reports. +#define STATEFUL_INITIALIZER(...) \ + stateful_initializer(::absl::types_internal::Generator( \ + [&] { return __VA_ARGS__; }, ABSL_INTERNAL_STRINGIZE(__VA_ARGS__))) + +// Used in the builder-pattern. +// +// Takes a series of INITIALIZER and/or STATEFUL_INITIALIZER invocations and +// forwards them along to be tested, grouping them such that the testing suite +// knows that they are supposed to represent the same logical value (the values +// compare the same, hash the same, etc.). +#define EQUIVALENCE_CLASS(...) \ + equivalence_class(ABSL_INTERNAL_TRANSFORM_ARGS( \ + ABSL_INTERNAL_PREPEND_EQ_MAKER, __VA_ARGS__)) + +// An invocation of this or WITH_STRICT_PROFILE must end the builder-pattern. +// It takes a Profile as its argument. +// +// This executes the tests and allows types that are "more referined" than the +// profile specifies, but not less. For instance, if the Profile specifies +// noexcept copy-constructiblity, the test will fail if the copy-constructor is +// not noexcept, however, it will succeed if the copy constructor is trivial. +// +// This is useful for testing that a type meets some minimum set of +// requirements. +#define WITH_LOOSE_PROFILE(...) \ + with_loose_profile( \ + ::absl::types_internal::MakeLooseProfileRangeT<__VA_ARGS__>()) \ + ABSL_INTERNAL_RPAREN ABSL_INTERNAL_RPAREN; \ + else GTEST_FATAL_FAILURE_(gtest_ar.failure_message()) // NOLINT + +// An invocation of this or WITH_STRICT_PROFILE must end the builder-pattern. +// It takes a Domain and a Profile as its arguments. +// +// This executes the tests and disallows types that differ at all from the +// properties of the Profile. For instance, if the Profile specifies noexcept +// copy-constructiblity, the test will fail if the copy constructor is trivial. +// +// This is useful for testing that a type does not do anything more than a +// specification requires, such as to minimize things like Hyrum's Law, or more +// commonly, to prevent a type from being "accidentally" copy-constructible in +// a way that may produce incorrect results, simply because the user forget to +// delete that operation. +#define WITH_STRICT_PROFILE(...) \ + with_strict_profile( \ + ::absl::types_internal::MakeStrictProfileRangeT<__VA_ARGS__>()) \ + ABSL_INTERNAL_RPAREN ABSL_INTERNAL_RPAREN; \ + else GTEST_FATAL_FAILURE_(gtest_ar.failure_message()) // NOLINT + +// Internal macro that is used in the internals of the EDSL when forming +// equivalence classes. +#define ABSL_INTERNAL_PREPEND_EQ_MAKER(arg) \ + ::absl::types_internal::EquivalenceClassMaker().arg + +} // namespace types_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_TYPES_INTERNAL_CONFORMANCE_TESTING_H_ diff --git a/Siv3D/include/ThirdParty/absl/types/internal/conformance_testing_helpers.h b/Siv3D/include/ThirdParty/absl/types/internal/conformance_testing_helpers.h new file mode 100644 index 0000000000..00775f960c --- /dev/null +++ b/Siv3D/include/ThirdParty/absl/types/internal/conformance_testing_helpers.h @@ -0,0 +1,391 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_TYPES_INTERNAL_CONFORMANCE_TESTING_HELPERS_H_ +#define ABSL_TYPES_INTERNAL_CONFORMANCE_TESTING_HELPERS_H_ + +// Checks to determine whether or not we can use abi::__cxa_demangle +#if (defined(__ANDROID__) || defined(ANDROID)) && !defined(OS_ANDROID) +#define ABSL_INTERNAL_OS_ANDROID +#endif + +// We support certain compilers only. See demangle.h for details. +#if defined(OS_ANDROID) && (defined(__i386__) || defined(__x86_64__)) +#define ABSL_TYPES_INTERNAL_HAS_CXA_DEMANGLE 0 +#elif (__GNUC__ >= 4 || (__GNUC__ >= 3 && __GNUC_MINOR__ >= 4)) && \ + !defined(__mips__) +#define ABSL_TYPES_INTERNAL_HAS_CXA_DEMANGLE 1 +#elif defined(__clang__) && !defined(_MSC_VER) +#define ABSL_TYPES_INTERNAL_HAS_CXA_DEMANGLE 1 +#else +#define ABSL_TYPES_INTERNAL_HAS_CXA_DEMANGLE 0 +#endif + +#include +#include +#include + +#include "absl/meta/type_traits.h" +#include "absl/strings/string_view.h" +#include "absl/utility/utility.h" + +#if ABSL_TYPES_INTERNAL_HAS_CXA_DEMANGLE +#include + +#include +#endif + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace types_internal { + +// Return a readable name for type T. +template +absl::string_view NameOfImpl() { +// TODO(calabrese) Investigate using debugging:internal_demangle as a fallback. +#if ABSL_TYPES_INTERNAL_HAS_CXA_DEMANGLE + int status = 0; + char* demangled_name = nullptr; + + demangled_name = + abi::__cxa_demangle(typeid(T).name(), nullptr, nullptr, &status); + + if (status == 0 && demangled_name != nullptr) { + return demangled_name; + } else { + return typeid(T).name(); + } +#else + return typeid(T).name(); +#endif + // NOTE: We intentionally leak demangled_name so that it remains valid + // throughout the remainder of the program. +} + +// Given a type, returns as nice of a type name as we can produce (demangled). +// +// Note: This currently strips cv-qualifiers and references, but that is okay +// because we only use this internally with unqualified object types. +template +std::string NameOf() { + static const absl::string_view result = NameOfImpl(); + return std::string(result); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Metafunction to check if a type is callable with no explicit arguments +template +struct IsNullaryCallableImpl : std::false_type {}; + +template +struct IsNullaryCallableImpl< + Fun, absl::void_t()())>> + : std::true_type { + using result_type = decltype(std::declval()()); + + template + using for_type = std::is_same; + + using void_if_true = void; +}; + +template +struct IsNullaryCallable : IsNullaryCallableImpl {}; +// +//////////////////////////////////////////////////////////////////////////////// + +// A type that contains a function object that returns an instance of a type +// that is undergoing conformance testing. This function is required to always +// return the same value upon invocation. +template +struct GeneratorType; + +// A type that contains a tuple of GeneratorType where each Fun has the +// same return type. The result of each of the different generators should all +// be equal values, though the underlying object representation may differ (such +// as if one returns 0.0 and another return -0.0, or if one returns an empty +// vector and another returns an empty vector with a different capacity. +template +struct EquivalenceClassType; + +//////////////////////////////////////////////////////////////////////////////// +// +// A metafunction to check if a type is a specialization of EquivalenceClassType +template +struct IsEquivalenceClass : std::false_type {}; + +template <> +struct IsEquivalenceClass> : std::true_type { + using self = IsEquivalenceClass; + + // A metafunction to check if this EquivalenceClassType is a valid + // EquivalenceClassType for a type `ValueType` that is undergoing testing + template + using for_type = std::true_type; +}; + +template +struct IsEquivalenceClass> + : std::true_type { + using self = IsEquivalenceClass; + + // The type undergoing conformance testing that this EquivalenceClass + // corresponds to + using result_type = typename IsNullaryCallable::result_type; + + // A metafunction to check if this EquivalenceClassType is a valid + // EquivalenceClassType for a type `ValueType` that is undergoing testing + template + using for_type = std::is_same; +}; +// +//////////////////////////////////////////////////////////////////////////////// + +// A type that contains an ordered series of EquivalenceClassTypes, where the +// the function object of each underlying GeneratorType has the same return type +// +// These equivalence classes are required to be in a logical ascending order +// that is consistent with comparison operators that are defined for the return +// type of each GeneratorType, if any. +template +struct OrderedEquivalenceClasses; + +//////////////////////////////////////////////////////////////////////////////// +// +// A metafunction to determine the return type of the function object contained +// in a GeneratorType specialization. +template +struct ResultOfGenerator {}; + +template +struct ResultOfGenerator> { + using type = decltype(std::declval()()); +}; + +template +using ResultOfGeneratorT = typename ResultOfGenerator>::type; +// +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +// +// A metafunction that yields true iff each of Funs is a GeneratorType +// specialization and they all contain functions with the same return type +template +struct AreGeneratorsWithTheSameReturnTypeImpl : std::false_type {}; + +template <> +struct AreGeneratorsWithTheSameReturnTypeImpl : std::true_type {}; + +template +struct AreGeneratorsWithTheSameReturnTypeImpl< + typename std::enable_if, ResultOfGeneratorT>...>::value>::type, + Head, Tail...> : std::true_type {}; + +template +struct AreGeneratorsWithTheSameReturnType + : AreGeneratorsWithTheSameReturnTypeImpl::type {}; +// +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +// +// A metafunction that yields true iff each of Funs is an EquivalenceClassType +// specialization and they all contain GeneratorType specializations that have +// the same return type +template +struct AreEquivalenceClassesOfTheSameType { + static_assert(sizeof...(EqClasses) != sizeof...(EqClasses), ""); +}; + +template <> +struct AreEquivalenceClassesOfTheSameType<> : std::true_type { + using self = AreEquivalenceClassesOfTheSameType; + + // Metafunction to check that a type is the same as all of the equivalence + // classes, if any. + // Note: In this specialization there are no equivalence classes, so the + // value type is always compatible. + template + using for_type = std::true_type; +}; + +template +struct AreEquivalenceClassesOfTheSameType> + : std::true_type { + using self = AreEquivalenceClassesOfTheSameType; + + // Metafunction to check that a type is the same as all of the equivalence + // classes, if any. + template + using for_type = typename IsEquivalenceClass< + EquivalenceClassType>::template for_type; +}; + +template +struct AreEquivalenceClassesOfTheSameType< + EquivalenceClassType<>, EquivalenceClassType<>, TailEqClasses...> + : AreEquivalenceClassesOfTheSameType::self {}; + +template +struct AreEquivalenceClassesOfTheSameType< + EquivalenceClassType<>, EquivalenceClassType, + TailEqClasses...> + : AreEquivalenceClassesOfTheSameType< + EquivalenceClassType, + TailEqClasses...>::self {}; + +template +struct AreEquivalenceClassesOfTheSameType< + EquivalenceClassType, EquivalenceClassType<>, + TailEqClasses...> + : AreEquivalenceClassesOfTheSameType< + EquivalenceClassType, + TailEqClasses...>::self {}; + +template +struct AreEquivalenceClassesOfTheSameType< + EquivalenceClassType, + EquivalenceClassType, TailEqClasses...> + : absl::conditional_t< + IsNullaryCallable::template for_type< + typename IsNullaryCallable::result_type>::value, + AreEquivalenceClassesOfTheSameType< + EquivalenceClassType, + TailEqClasses...>, + std::false_type> {}; +// +//////////////////////////////////////////////////////////////////////////////// + +// Execute a function for each passed-in parameter. +template +void ForEachParameter(const Fun& fun, const Cases&... cases) { + const std::initializer_list results = { + (static_cast(fun(cases)), true)...}; + + (void)results; +} + +// Execute a function on each passed-in parameter (using a bound function). +template +struct ForEachParameterFun { + template + void operator()(const T&... cases) const { + (ForEachParameter)(fun, cases...); + } + + Fun fun; +}; + +// Execute a function on each element of a tuple. +template +void ForEachTupleElement(const Fun& fun, const Tup& tup) { + absl::apply(ForEachParameterFun{fun}, tup); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Execute a function for each combination of two elements of a tuple, including +// combinations of an element with itself. +template +struct ForEveryTwoImpl { + template + struct WithBoundLhs { + template + void operator()(const Rhs& rhs) const { + fun(lhs, rhs); + } + + Fun fun; + Lhs lhs; + }; + + template + void operator()(const Lhs& lhs) const { + (ForEachTupleElement)(WithBoundLhs{fun, lhs}, args); + } + + Fun fun; + std::tuple args; +}; + +template +void ForEveryTwo(const Fun& fun, std::tuple args) { + (ForEachTupleElement)(ForEveryTwoImpl{fun, args}, args); +} +// +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +// +// Insert all values into an associative container +template +void InsertEach(Container* cont) { +} + +template +void InsertEach(Container* cont, H&& head, T&&... tail) { + cont->insert(head); + (InsertEach)(cont, tail...); +} +// +//////////////////////////////////////////////////////////////////////////////// +// A template with a nested "Invoke" static-member-function that executes a +// passed-in Callable when `Condition` is true, otherwise it ignores the +// Callable. This is useful for executing a function object with a condition +// that corresponds to whether or not the Callable can be safely instantiated. +// It has some overlapping uses with C++17 `if constexpr`. +template +struct If; + +template <> +struct If { + template + static void Invoke(const Fun& /*fun*/, P&&... /*args*/) {} +}; + +template <> +struct If { + template + static void Invoke(const Fun& fun, P&&... args) { + // TODO(calabrese) Use std::invoke equivalent instead of function-call. + fun(absl::forward

(args)...); + } +}; + +// +// ABSL_INTERNAL_STRINGIZE(...) +// +// This variadic macro transforms its arguments into a c-string literal after +// expansion. +// +// Example: +// +// ABSL_INTERNAL_STRINGIZE(std::array) +// +// Results in: +// +// "std::array" +#define ABSL_INTERNAL_STRINGIZE(...) ABSL_INTERNAL_STRINGIZE_IMPL((__VA_ARGS__)) +#define ABSL_INTERNAL_STRINGIZE_IMPL(arg) ABSL_INTERNAL_STRINGIZE_IMPL2 arg +#define ABSL_INTERNAL_STRINGIZE_IMPL2(...) #__VA_ARGS__ + +} // namespace types_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_TYPES_INTERNAL_CONFORMANCE_TESTING_HELPERS_H_ diff --git a/Siv3D/include/ThirdParty/absl/types/internal/optional.h b/Siv3D/include/ThirdParty/absl/types/internal/optional.h index 92932b6001..a96d260a43 100644 --- a/Siv3D/include/ThirdParty/absl/types/internal/optional.h +++ b/Siv3D/include/ThirdParty/absl/types/internal/optional.h @@ -25,34 +25,6 @@ #include "absl/meta/type_traits.h" #include "absl/utility/utility.h" -// ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS -// -// Inheriting constructors is supported in GCC 4.8+, Clang 3.3+ and MSVC 2015. -// __cpp_inheriting_constructors is a predefined macro and a recommended way to -// check for this language feature, but GCC doesn't support it until 5.0 and -// Clang doesn't support it until 3.6. -// Also, MSVC 2015 has a bug: it doesn't inherit the constexpr template -// constructor. For example, the following code won't work on MSVC 2015 Update3: -// struct Base { -// int t; -// template -// constexpr Base(T t_) : t(t_) {} -// }; -// struct Foo : Base { -// using Base::Base; -// } -// constexpr Foo foo(0); // doesn't work on MSVC 2015 -#if defined(__clang__) -#if __has_feature(cxx_inheriting_constructors) -#define ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS 1 -#endif -#elif (defined(__GNUC__) && \ - (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 8)) || \ - (__cpp_inheriting_constructors >= 200802) || \ - (defined(_MSC_VER) && _MSC_VER >= 1910) -#define ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS 1 -#endif - namespace absl { ABSL_NAMESPACE_BEGIN @@ -91,7 +63,15 @@ class optional_data_dtor_base { void destruct() noexcept { if (engaged_) { + // `data_` must be initialized if `engaged_` is true. +#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif data_.~T(); +#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0) +#pragma GCC diagnostic pop +#endif engaged_ = false; } } @@ -137,15 +117,7 @@ template class optional_data_base : public optional_data_dtor_base { protected: using base = optional_data_dtor_base; -#ifdef ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS using base::base; -#else - optional_data_base() = default; - - template - constexpr explicit optional_data_base(in_place_t t, Args&&... args) - : base(t, absl::forward(args)...) {} -#endif template void construct(Args&&... args) { @@ -180,27 +152,13 @@ class optional_data; template class optional_data : public optional_data_base { protected: -#ifdef ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS using optional_data_base::optional_data_base; -#else - optional_data() = default; - - template - constexpr explicit optional_data(in_place_t t, Args&&... args) - : optional_data_base(t, absl::forward(args)...) {} -#endif }; template class optional_data : public optional_data_base { protected: -#ifdef ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS using optional_data_base::optional_data_base; -#else - template - constexpr explicit optional_data(in_place_t t, Args&&... args) - : optional_data_base(t, absl::forward(args)...) {} -#endif optional_data() = default; @@ -391,6 +349,4 @@ struct optional_hash_base >()( ABSL_NAMESPACE_END } // namespace absl -#undef ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS - #endif // ABSL_TYPES_INTERNAL_OPTIONAL_H_ diff --git a/Siv3D/include/ThirdParty/absl/types/internal/parentheses.h b/Siv3D/include/ThirdParty/absl/types/internal/parentheses.h new file mode 100644 index 0000000000..5aebee8fde --- /dev/null +++ b/Siv3D/include/ThirdParty/absl/types/internal/parentheses.h @@ -0,0 +1,34 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// parentheses.h +// ----------------------------------------------------------------------------- +// +// This file contains macros that expand to a left parenthesis and a right +// parenthesis. These are in their own file and are generated from macros +// because otherwise clang-format gets confused and clang-format off directives +// do not help. +// +// The parentheses macros are used when wanting to require a rescan before +// expansion of parenthesized text appearing after a function-style macro name. + +#ifndef ABSL_TYPES_INTERNAL_PARENTHESES_H_ +#define ABSL_TYPES_INTERNAL_PARENTHESES_H_ + +#define ABSL_INTERNAL_LPAREN ( + +#define ABSL_INTERNAL_RPAREN ) + +#endif // ABSL_TYPES_INTERNAL_PARENTHESES_H_ diff --git a/Siv3D/include/ThirdParty/absl/types/internal/span.h b/Siv3D/include/ThirdParty/absl/types/internal/span.h index 112612f4bd..ab89ba3ca7 100644 --- a/Siv3D/include/ThirdParty/absl/types/internal/span.h +++ b/Siv3D/include/ThirdParty/absl/types/internal/span.h @@ -28,10 +28,10 @@ namespace absl { ABSL_NAMESPACE_BEGIN -namespace span_internal { -// A constexpr min function -constexpr size_t Min(size_t a, size_t b) noexcept { return a < b ? a : b; } +template +class Span; +namespace span_internal { // Wrappers for access to container data pointers. template constexpr auto GetDataImpl(C& c, char) noexcept // NOLINT(runtime/references) @@ -88,7 +88,7 @@ using EnableIfMutable = template