use std::cmp::Ordering;
use crate::GcRef;
use crate::descriptor::{FormatSink, TypeDescriptor};
use crate::maps::render_into;
pub(crate) unsafe fn container_cmp(a: GcRef, b: GcRef) -> Ordering {
unsafe { slot_cmp(a, b, a.descriptor(), b.descriptor()) }
}
pub(crate) unsafe fn slot_cmp(
a: GcRef,
b: GcRef,
da: &'static TypeDescriptor,
db: &'static TypeDescriptor,
) -> Ordering {
let (ida, idb) = (da.id().to_u32(), db.id().to_u32());
if ida != idb {
return ida.cmp(&idb);
}
match da.compare {
Some(compare) => unsafe {
compare(
a.payload::<u8>() as *const u8,
b.payload::<u8>() as *const u8,
)
},
None => unsafe { rendered_cmp(a, da, b, db) },
}
}
unsafe fn rendered_cmp(a: GcRef, da: &TypeDescriptor, b: GcRef, db: &TypeDescriptor) -> Ordering {
let mut left = String::new();
let mut right = String::new();
unsafe {
render_into(&mut FormatSink::display(&mut left), da, a);
render_into(&mut FormatSink::display(&mut right), db, b);
}
left.cmp(&right)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Runtime;
use crate::records::{RecordField, RecordSchema, SchemaIdentity};
use crate::tuples::TupleSchema;
fn one_of_every_key_type(rt: &mut Runtime) -> Vec<GcRef> {
let tuple_schema: &'static TupleSchema = Box::leak(Box::new(TupleSchema {
descriptors: Box::leak(
vec![
&crate::scalars::INT as *const TypeDescriptor,
&crate::text::TEXT as *const TypeDescriptor,
]
.into_boxed_slice(),
),
}));
let record_schema: &'static RecordSchema = Box::leak(Box::new(RecordSchema {
identity: SchemaIdentity::Nominal("Point"),
fields: Box::leak(
vec![RecordField {
name: "x",
descriptor: &crate::scalars::INT,
}]
.into_boxed_slice(),
),
}));
let scalars = vec![
rt.alloc_int(3),
rt.alloc_int(-1),
rt.alloc_byte(200),
rt.alloc_char('z' as u32),
rt.alloc_float(f64::NAN),
rt.alloc_float(2.5),
rt.alloc_text("abc"),
rt.alloc_bool(true),
rt.alloc_bool(false),
rt.alloc_unit(),
];
let (one, five, seven, seven_text) = (
rt.alloc_int(1),
rt.alloc_int(5),
rt.alloc_int(7),
rt.alloc_text("seven"),
);
let mut ctx = rt.context();
let mut out = scalars;
unsafe {
out.push(crate::abi::praxis_range_new(&mut ctx, one, five));
let tuple = crate::abi::praxis_alloc_tuple(&mut ctx, tuple_schema);
crate::abi::praxis_tuple_set(&mut ctx, tuple, 0, seven);
crate::abi::praxis_tuple_set(&mut ctx, tuple, 1, seven_text);
out.push(tuple);
let record = crate::abi::praxis_alloc_record(&mut ctx, record_schema);
crate::abi::praxis_record_set_field(&mut ctx, record, 0, one);
out.push(record);
out.push(crate::abi::praxis_alloc_enum(
&mut ctx,
crate::enums::option_schema(),
0,
));
out.push(crate::abi::praxis_vec_new(&mut ctx, &crate::scalars::INT));
}
out
}
#[test]
fn container_cmp_is_a_total_order_over_every_key_type() {
let mut rt = Runtime::new();
let values = one_of_every_key_type(&mut rt);
let cmp = |a: GcRef, b: GcRef| unsafe { container_cmp(a, b) };
for &a in &values {
assert_eq!(cmp(a, a), Ordering::Equal, "reflexive");
for &b in &values {
assert_eq!(
cmp(a, b),
cmp(b, a).reverse(),
"antisymmetric: {} vs {}",
a.descriptor().name,
b.descriptor().name
);
for &c in &values {
if cmp(a, b) != Ordering::Greater && cmp(b, c) != Ordering::Greater {
assert_ne!(
cmp(a, c),
Ordering::Greater,
"transitive through {}",
b.descriptor().name
);
}
}
}
}
}
#[test]
fn values_of_different_types_order_by_descriptor_id_and_not_by_address() {
let rt = Runtime::new();
let int = rt.alloc_int(1);
let text = rt.alloc_text("1");
let expected = crate::scalars::INT
.id()
.to_u32()
.cmp(&crate::text::TEXT.id().to_u32());
assert_eq!(unsafe { container_cmp(int, text) }, expected);
let rt2 = Runtime::new();
assert_eq!(
unsafe { container_cmp(rt2.alloc_int(1), rt2.alloc_text("1")) },
expected
);
}
#[test]
fn a_value_whose_type_has_no_order_still_orders_deterministically() {
let mut rt = Runtime::new();
assert!(
!crate::collections::VEC.is_orderable(),
"a Vec can never be a key, so it carries no compare (ADR-057 D4)"
);
let (one, two) = (rt.alloc_int(1), rt.alloc_int(2));
let mut ctx = rt.context();
let (ones, twos) = unsafe {
let ones = crate::abi::praxis_vec_new(&mut ctx, &crate::scalars::INT);
let twos = crate::abi::praxis_vec_new(&mut ctx, &crate::scalars::INT);
crate::abi::praxis_vec_push(&mut ctx, ones, one);
crate::abi::praxis_vec_push(&mut ctx, twos, two);
(ones, twos)
};
assert_eq!(unsafe { container_cmp(ones, twos) }, Ordering::Less);
assert_eq!(unsafe { container_cmp(ones, twos) }, Ordering::Less);
assert_eq!(unsafe { container_cmp(twos, ones) }, Ordering::Greater);
}
}