Created
October 10, 2021 12:24
-
-
Save tesaguri/e14a9733a482869bb5665675838ce3c9 to your computer and use it in GitHub Desktop.
`x.to_string().cmp(&y.to_string())` without allocating the string (not throughly tested yet)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::cmp::Ordering; | |
use std::fmt::{self, Display, Write}; | |
struct FmtCmp<T>(T); | |
impl<T: Display> PartialEq for FmtCmp<T> { | |
fn eq(&self, other: &Self) -> bool { | |
self.cmp(other) == Ordering::Equal | |
} | |
} | |
impl<T: Display> Eq for FmtCmp<T> {} | |
impl<T: Display> PartialOrd for FmtCmp<T> { | |
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { | |
Some(self.cmp(other)) | |
} | |
} | |
impl<T: Display> Ord for FmtCmp<T> { | |
fn cmp(&self, other: &Self) -> Ordering { | |
struct Lhs<T> { | |
this: T, | |
/// Byte position in `this.to_string()` that we are reading. | |
pos: usize, | |
other_len: usize, | |
ret: Ordering, | |
} | |
let mut adapter = Lhs { | |
this: &self.0, | |
pos: 0, | |
other_len: 0, | |
ret: Ordering::Equal, | |
}; | |
let _ = write!(&mut adapter, "{}", &other.0); | |
return adapter.ret.then(adapter.pos.cmp(&adapter.other_len)); | |
impl<T: Display> Write for Lhs<T> { | |
fn write_str(&mut self, other: &str) -> fmt::Result { | |
struct Rhs<'a> { | |
other: &'a [u8], | |
/// Number of bytes to skip until we get to `this.to_string()[pos]`. | |
skip: usize, | |
ret: &'a mut Ordering, | |
} | |
let mut adapter = Rhs { | |
other: other.as_bytes(), | |
skip: self.pos, | |
ret: &mut self.ret, | |
}; | |
write!(&mut adapter, "{}", self.this)?; | |
if !adapter.other.is_empty() { | |
// `other` is longer than `this`. | |
self.ret = self.ret.then(Ordering::Less); | |
} | |
if self.ret != Ordering::Equal { | |
// Short-circuit by returning an error. | |
return Err(fmt::Error); | |
} | |
self.pos += other.len(); | |
self.other_len += other.len(); | |
return Ok(()); | |
impl<'a> Write for Rhs<'a> { | |
fn write_str(&mut self, this: &str) -> fmt::Result { | |
let skip = self.skip.min(this.len()); | |
self.skip -= skip; | |
let this = &this.as_bytes()[skip..]; | |
let read = this.len().min(self.other.len()); | |
*self.ret = this.cmp(&self.other[0..read]); | |
if *self.ret != Ordering::Equal { | |
return Err(fmt::Error); | |
} | |
self.other = &self.other[read..]; | |
Ok(()) | |
} | |
} | |
} | |
} | |
} | |
} | |
#[cfg(test)] | |
mod tests { | |
use super::*; | |
#[test] | |
fn fmt_cmp() { | |
#[track_caller] | |
fn check<T: Display>(a: T, b: T) { | |
assert_eq!( | |
FmtCmp(&a).cmp(&FmtCmp(&b)), | |
a.to_string().cmp(&b.to_string()), | |
); | |
} | |
check("", ""); | |
check(42, 42); | |
check(42, 240); | |
check(240, 42); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment