Skip to content

Commit f0037dd

Browse files
committed
sort-scalars
1 parent f5f5286 commit f0037dd

File tree

2 files changed

+107
-10
lines changed

2 files changed

+107
-10
lines changed

libcxx/include/__algorithm/radix_sort.h

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,11 @@
2929

3030
#include <__algorithm/for_each.h>
3131
#include <__algorithm/move.h>
32+
#include <__bit/bit_cast.h>
3233
#include <__bit/bit_log2.h>
3334
#include <__bit/countl.h>
3435
#include <__config>
36+
#include <__cstddef/size_t.h>
3537
#include <__functional/identity.h>
3638
#include <__iterator/access.h>
3739
#include <__iterator/distance.h>
@@ -44,9 +46,12 @@
4446
#include <__type_traits/enable_if.h>
4547
#include <__type_traits/invoke.h>
4648
#include <__type_traits/is_assignable.h>
49+
#include <__type_traits/is_enum.h>
50+
#include <__type_traits/is_floating_point.h>
4751
#include <__type_traits/is_integral.h>
4852
#include <__type_traits/is_unsigned.h>
4953
#include <__type_traits/make_unsigned.h>
54+
#include <__type_traits/underlying_type.h>
5055
#include <__utility/forward.h>
5156
#include <__utility/integer_sequence.h>
5257
#include <__utility/move.h>
@@ -298,6 +303,94 @@ _LIBCPP_HIDE_FROM_ABI constexpr auto __shift_to_unsigned(_Ip __n) {
298303
return static_cast<make_unsigned_t<_Ip> >(__n ^ __min_value);
299304
}
300305

306+
template <size_t _Size>
307+
struct __unsigned_integer_of_size {};
308+
309+
template <>
310+
struct __unsigned_integer_of_size<1> {
311+
using type = uint8_t;
312+
};
313+
314+
template <>
315+
struct __unsigned_integer_of_size<2> {
316+
using type = uint16_t;
317+
};
318+
319+
template <>
320+
struct __unsigned_integer_of_size<4> {
321+
using type = uint32_t;
322+
};
323+
324+
template <>
325+
struct __unsigned_integer_of_size<8> {
326+
using type = uint64_t;
327+
};
328+
329+
template <>
330+
struct __unsigned_integer_of_size<16> {
331+
# if _LIBCPP_HAS_INT128
332+
using type = __int128;
333+
# endif
334+
};
335+
336+
template <size_t _Size>
337+
using __unsigned_integer_of_size_t _LIBCPP_NODEBUG = typename __unsigned_integer_of_size<_Size>::type;
338+
339+
template <class _Sc>
340+
using __unsigned_representation_for_t _LIBCPP_NODEBUG = __unsigned_integer_of_size_t<sizeof(_Sc)>;
341+
342+
// Represent a scalar type as an ordered integer
343+
344+
// The function is defined for ordered scalar types: integers, floating-point numbers, pointers, and enums.
345+
// Returns an integer representation such that for any `x` and `y` such that `x < y`, the expression
346+
// `__to_ordered_integral(x) < __to_ordered_integral(y)` is true, where `x`, `y` are values of the `Scalar` type.
347+
// __unsigned_representation_for_t<_Scalar> __to_ordered_integral(_Scalar);
348+
349+
template <class _Integral, enable_if_t< is_integral_v<_Integral>, int> = 0>
350+
_LIBCPP_HIDE_FROM_ABI constexpr auto __to_ordered_integral(_Integral __n) {
351+
return __n;
352+
}
353+
354+
// An overload for floating-point numbers
355+
356+
// From the IEEE 754 standard, we know that:
357+
// 1. The bit representation of positive floats directly reflects their order:
358+
// When comparing floats by magnitude, the number with the larger exponent is greater, and if the exponents are
359+
// equal, the one with the larger mantissa is greater.
360+
// 2. The bit representation of negative floats reflects their reverse order (for the same reasons).
361+
// 3. The most significant bit (sign bit) is zero for positive floats and one for negative floats. Therefore, in the raw
362+
// bit representation, any negative number will be greater than any positive number.
363+
364+
// The only exception from this rule is `NaN`, which is unordered by definition.
365+
366+
// Based on the above, to obtain correctly ordered integral representation of floating-point numbers, we need to:
367+
// 1. Invert the bit representation (including the sign bit) of negative floats to switch from reverse order to direct
368+
// order;
369+
// 2. Invert the sign bit for positive floats.
370+
371+
// Thus, in final integral representation, we have reversed the order for negative floats and made all negative floats
372+
// smaller than all positive numbers (by inverting the sign bit).
373+
template <class _Floating, enable_if_t< is_floating_point_v<_Floating>, int> = 0>
374+
_LIBCPP_HIDE_FROM_ABI constexpr auto __to_ordered_integral(_Floating __f) {
375+
using __integral_type = __unsigned_representation_for_t<_Floating>;
376+
constexpr auto __bit_count = std::numeric_limits<__integral_type>::digits;
377+
constexpr auto __sign_bit_mask = static_cast<__integral_type>(__integral_type{1} << (__bit_count - 1));
378+
379+
const auto __u = std::__bit_cast<__integral_type>(__f);
380+
381+
return static_cast<__integral_type>(__u & __sign_bit_mask ? ~__u : __u ^ __sign_bit_mask);
382+
}
383+
384+
template <class _Enum, enable_if_t< is_enum_v<_Enum>, int> = 0>
385+
_LIBCPP_HIDE_FROM_ABI constexpr auto __to_ordered_integral(_Enum __e) {
386+
return static_cast<std::underlying_type_t<_Enum>>(__e);
387+
}
388+
389+
template <class _Pointer>
390+
_LIBCPP_HIDE_FROM_ABI constexpr auto __to_ordered_integral(_Pointer* __ptr) {
391+
return std::__bit_cast<__unsigned_representation_for_t<_Pointer*>>(__ptr);
392+
}
393+
301394
struct __low_byte_fn {
302395
template <class _Ip>
303396
_LIBCPP_HIDE_FROM_ABI constexpr uint8_t operator()(_Ip __integer) const {
@@ -314,7 +407,9 @@ __radix_sort(_RandomAccessIterator1 __first,
314407
_RandomAccessIterator2 __buffer,
315408
_Map __map,
316409
_Radix __radix) {
317-
auto __map_to_unsigned = [__map = std::move(__map)](const auto& __x) { return std::__shift_to_unsigned(__map(__x)); };
410+
auto __map_to_unsigned = [__map = std::move(__map)](const auto& __x) {
411+
return std::__shift_to_unsigned(__map(std::__to_ordered_integral(__x)));
412+
};
318413
std::__radix_sort_impl(__first, __last, __buffer, __map_to_unsigned, __radix);
319414
}
320415

libcxx/include/__algorithm/stable_sort.h

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,10 @@
2525
#include <__memory/unique_temporary_buffer.h>
2626
#include <__type_traits/desugars_to.h>
2727
#include <__type_traits/enable_if.h>
28+
#include <__type_traits/invoke.h>
2829
#include <__type_traits/is_constant_evaluated.h>
29-
#include <__type_traits/is_integral.h>
3030
#include <__type_traits/is_same.h>
31+
#include <__type_traits/is_scalar.h>
3132
#include <__type_traits/is_trivially_assignable.h>
3233
#include <__type_traits/remove_cvref.h>
3334
#include <__utility/move.h>
@@ -202,7 +203,7 @@ struct __stable_sort_switch {
202203
#if _LIBCPP_STD_VER >= 17
203204
template <class _Tp>
204205
_LIBCPP_HIDE_FROM_ABI constexpr unsigned __radix_sort_min_bound() {
205-
static_assert(is_integral<_Tp>::value);
206+
static_assert(is_scalar<_Tp>::value);
206207
if constexpr (sizeof(_Tp) == 1) {
207208
return 1 << 8;
208209
}
@@ -212,13 +213,14 @@ _LIBCPP_HIDE_FROM_ABI constexpr unsigned __radix_sort_min_bound() {
212213

213214
template <class _Tp>
214215
_LIBCPP_HIDE_FROM_ABI constexpr unsigned __radix_sort_max_bound() {
215-
static_assert(is_integral<_Tp>::value);
216+
static_assert(is_scalar<_Tp>::value);
216217
if constexpr (sizeof(_Tp) >= 8) {
217218
return 1 << 15;
218219
}
219220

220221
return 1 << 16;
221222
}
223+
222224
#endif // _LIBCPP_STD_VER >= 17
223225

224226
template <class _AlgPolicy, class _Compare, class _RandomAccessIterator>
@@ -246,12 +248,12 @@ _LIBCPP_CONSTEXPR_SINCE_CXX26 void __stable_sort(
246248
}
247249

248250
#if _LIBCPP_STD_VER >= 17
249-
constexpr auto __default_comp =
250-
__desugars_to_v<__totally_ordered_less_tag, __remove_cvref_t<_Compare>, value_type, value_type >;
251-
constexpr auto __integral_value =
252-
is_integral_v<value_type > && is_same_v< value_type&, __iter_reference<_RandomAccessIterator>>;
253-
constexpr auto __allowed_radix_sort = __default_comp && __integral_value;
254-
if constexpr (__allowed_radix_sort) {
251+
constexpr auto __default_comp = __desugars_to_v<__less_tag, __remove_cvref_t<_Compare>, value_type, value_type >;
252+
constexpr auto __scalar_value =
253+
is_scalar_v<value_type > && is_same_v< value_type&, __iter_reference<_RandomAccessIterator>>;
254+
// There are non-comparable scalars (std::nullptr_t, pointers to members), so we need to exclude them.
255+
constexpr auto __comparable_value = is_invocable_r_v<bool, _Compare, value_type, value_type>;
256+
if constexpr (__default_comp && __scalar_value && __comparable_value) {
255257
if (__len <= __buff_size && __len >= static_cast<difference_type>(std::__radix_sort_min_bound<value_type>()) &&
256258
__len <= static_cast<difference_type>(std::__radix_sort_max_bound<value_type>())) {
257259
if (__libcpp_is_constant_evaluated()) {

0 commit comments

Comments
 (0)