blueprint_cmp_any/
ord.rs

1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is dual-licensed under either the MIT license found in the
5 * LICENSE-MIT file in the root directory of this source tree or the Apache
6 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
7 * of this source tree. You may select, at your option, one of the
8 * above-listed licenses.
9 */
10
11use std::any::Any;
12use std::any::TypeId;
13use std::cmp::Ordering;
14use std::marker::PhantomData;
15
16/// Ordering between arbitrary types.
17pub struct OrdAny<'a> {
18    type_id: TypeId,
19    cmp: unsafe fn(*const (), *const ()) -> Ordering,
20    val: *const (),
21    _marker: PhantomData<&'a dyn Any>,
22}
23
24impl<'a> OrdAny<'a> {
25    #[inline]
26    pub fn new<A: Ord + 'static>(a: &'a A) -> Self {
27        OrdAny {
28            type_id: TypeId::of::<A>(),
29            cmp: |this, other| {
30                // SAFETY: We only call `cmp` with
31                //   `this.type_id == other.type_id == TypeId::of::<A>()`.
32                let this = unsafe { &*(this as *const A) };
33                let other = unsafe { &*(other as *const A) };
34                this.cmp(other)
35            },
36            val: a as *const A as *const (),
37            _marker: PhantomData,
38        }
39    }
40
41    /// Get `TypeId` of the referenced type.
42    #[inline]
43    pub fn type_id(&self) -> TypeId {
44        self.type_id
45    }
46}
47
48impl<'a> PartialEq for OrdAny<'a> {
49    #[inline]
50    fn eq(&self, other: &OrdAny<'a>) -> bool {
51        self.cmp(other) == Ordering::Equal
52    }
53}
54
55impl Eq for OrdAny<'_> {}
56
57impl<'a> PartialOrd for OrdAny<'a> {
58    #[inline]
59    fn partial_cmp(&self, other: &OrdAny<'a>) -> Option<Ordering> {
60        Some(self.cmp(other))
61    }
62}
63
64/// Compare by type id first, then by value.
65impl Ord for OrdAny<'_> {
66    #[inline]
67    fn cmp(&self, other: &Self) -> Ordering {
68        let type_cmp = self.type_id.cmp(&other.type_id);
69        if type_cmp != Ordering::Equal {
70            return type_cmp;
71        }
72        unsafe { (self.cmp)(self.val, other.val) }
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use std::any::TypeId;
79    use std::cmp::Ordering;
80
81    use crate::OrdAny;
82
83    #[test]
84    fn test_ord_any() {
85        assert!(OrdAny::new(&1) < OrdAny::new(&2));
86        assert!(OrdAny::new(&true) > OrdAny::new(&false));
87        assert_eq!(
88            Ordering::Equal,
89            OrdAny::new(&String::new()).cmp(&OrdAny::new(&String::new()))
90        );
91        assert_eq!(
92            OrdAny::new(&1i32) < OrdAny::new(&true),
93            TypeId::of::<i32>() < TypeId::of::<bool>()
94        );
95    }
96}