From: Simon Tatham Date: Sat, 12 Apr 2025 12:34:09 +0000 (+0100) Subject: Stop allocating a Vec for a one-word nimber X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ian/git?a=commitdiff_plain;h=3d042562c6321e134c71c1948bc8e4ed1c717c86;p=nimber.git Stop allocating a Vec for a one-word nimber --- diff --git a/src/finitenimber.rs b/src/finitenimber.rs index 469cd06..cb76756 100644 --- a/src/finitenimber.rs +++ b/src/finitenimber.rs @@ -9,32 +9,47 @@ const WORDLEVELS: usize = 6; // 2^{2^6} = 64 = size of Word /// A type representing a finite nimber. #[derive(Clone, PartialEq, Eq, Hash)] -pub struct FiniteNimber(Vec); +pub struct FiniteNimber(FiniteNimberEnum); + +/// A type representing a finite nimber. +#[derive(Clone, PartialEq, Eq, Hash)] +enum FiniteNimberEnum { + Single(Word), + Vector(Vec), +} #[derive(Clone, Copy)] pub enum FiniteNimberRef<'a> { - OneWord(Word), + Single(Word), Slice(&'a [Word]), } -use FiniteNimberRef::*; impl Debug for FiniteNimber { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), core::fmt::Error> { - write!(f, "FiniteNimber[")?; - let mut sep: &str = ""; - for v in &self.0 { - write!(f, "{}0x{:016x}", sep, v)?; - sep = ", "; + match &self.0 { + FiniteNimberEnum::Single(v) => { + write!(f, "FiniteNimberEnum::Single(0x{:16x})", v) + } + FiniteNimberEnum::Vector(s) => { + write!(f, "FiniteNimberEnum::Vector[")?; + let mut sep: &str = ""; + for v in s { + write!(f, "{}0x{:016x}", sep, v)?; + sep = ", "; + } + write!(f, "]") + } } - write!(f, "]") } } impl Debug for FiniteNimberRef<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), core::fmt::Error> { match self { - OneWord(v) => write!(f, "FiniteNimberRef::OneWord(0x{:16x})", v), - Slice(s) => { + FiniteNimberRef::Single(v) => { + write!(f, "FiniteNimberRef::Single(0x{:16x})", v) + } + FiniteNimberRef::Slice(s) => { write!(f, "FiniteNimberRef::Slice[")?; let mut sep: &str = ""; for v in *s { @@ -49,15 +64,13 @@ impl Debug for FiniteNimberRef<'_> { impl Display for FiniteNimber { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), core::fmt::Error> { - if !f.alternate() - && self.to_ref().level() < WORDLEVELS - && self.0[0] < 10 - { - write!(f, "*{}", self.0[0]) + let r = self.to_ref(); + if !f.alternate() && r.level() < WORDLEVELS && r.low_word() < 10 { + write!(f, "*{}", r.low_word()) } else { write!(f, "*0x")?; let mut started = false; - for v in self.0.iter().rev() { + for v in r.as_slice().iter().rev() { if started { write!(f, "{:016x}", v)?; } else if *v != 0 { @@ -75,19 +88,19 @@ impl Display for FiniteNimber { impl Borrow<[Word]> for FiniteNimber { fn borrow(&self) -> &[Word] { - &self.0 + self.as_slice() } } impl<'a> FiniteNimberRef<'a> { fn zero() -> Self { - OneWord(0) + FiniteNimberRef::Single(0) } fn low_word(self) -> Word { match self { - OneWord(v) => v, - Slice(s) => { + FiniteNimberRef::Single(v) => v, + FiniteNimberRef::Slice(s) => { *(s.first().expect("FiniteNimberRef::Slice is never empty")) } } @@ -99,8 +112,8 @@ impl<'a> FiniteNimberRef<'a> { 'b: 'c, { match self { - OneWord(v) => core::slice::from_ref(v), - Slice(s) => s, + FiniteNimberRef::Single(v) => core::slice::from_ref(v), + FiniteNimberRef::Slice(s) => s, } } @@ -117,17 +130,12 @@ impl<'a> FiniteNimberRef<'a> { } match self { - OneWord(v) => word_level(v), - Slice(s) => { - match s - .iter() - .enumerate() - .rev() - .find(|(_index, word)| **word != 0) - { - None => 0, - Some((0, w)) => word_level(*w), - Some((i, _)) => usize_level(i) + (WORDLEVELS + 1), + FiniteNimberRef::Single(v) => word_level(v), + FiniteNimberRef::Slice(s) => { + let w = s.len().checked_sub(1).expect("slice representation must always be non-empty"); + match w { + 0 => word_level(s[0]), + w => usize_level(w) + (WORDLEVELS + 1), } } } @@ -143,11 +151,16 @@ impl<'a> FiniteNimberRef<'a> { let bits = 1 << (level as Word); let mask = (1 << bits) - 1; let v = self.low_word(); - return (OneWord(v & mask), OneWord((v >> bits) & mask)); + return ( + FiniteNimberRef::Single(v & mask), + FiniteNimberRef::Single((v >> bits) & mask), + ); } Some(wordlevel) => match self { - OneWord(v) => (OneWord(v), Self::zero()), - Slice(s) => { + FiniteNimberRef::Single(v) => { + (FiniteNimberRef::Single(v), Self::zero()) + } + FiniteNimberRef::Slice(s) => { match wordlevel .try_into() .ok() @@ -159,8 +172,13 @@ impl<'a> FiniteNimberRef<'a> { "FiniteNimberRef::Slice is never empty", ); match iter.next() { - Some(hi) => (Slice(lo), Slice(hi)), - None => (Slice(lo), Self::zero()), + Some(hi) => ( + FiniteNimberRef::Slice(lo), + FiniteNimberRef::Slice(hi), + ), + None => { + (FiniteNimberRef::Slice(lo), Self::zero()) + } } } None => (self.clone(), Self::zero()), @@ -190,16 +208,16 @@ impl<'a> FiniteNimberRef<'a> { let slo = self.as_slice(); let shi = hi.as_slice(); let padding = words.saturating_sub(slo.len()); - FiniteNimber( - slo.iter() - .cloned() - .take(words) - .chain(core::iter::repeat(0).take(padding)) - .chain(shi.iter().cloned().take(words)) - .collect(), - ) + let vec: Vec<_> = slo + .iter() + .cloned() + .take(words) + .chain(core::iter::repeat(0).take(padding)) + .chain(shi.iter().cloned().take(words)) + .collect(); + FiniteNimber::from(vec) } - None => FiniteNimber(self.as_slice().into()), + None => FiniteNimber::from(self.as_slice()), } } } @@ -208,9 +226,19 @@ impl<'a> FiniteNimberRef<'a> { impl FiniteNimber { fn to_ref(&self) -> FiniteNimberRef { - match self.0.len() { - 0 => FiniteNimberRef::zero(), - _ => Slice(&self.0), + match &self.0 { + FiniteNimberEnum::Single(w) => FiniteNimberRef::Single(*w), + FiniteNimberEnum::Vector(v) => match v.len() { + 0 => FiniteNimberRef::zero(), + _ => FiniteNimberRef::Slice(&v), + }, + } + } + + fn as_slice(&self) -> &[Word] { + match &self.0 { + FiniteNimberEnum::Single(w) => core::slice::from_ref(w), + FiniteNimberEnum::Vector(v) => &v, } } } @@ -223,25 +251,49 @@ impl<'a> From<&'a FiniteNimber> for FiniteNimberRef<'a> { impl From> for FiniteNimber { fn from(n: FiniteNimberRef) -> Self { - FiniteNimber(n.as_slice().into()) + match n { + FiniteNimberRef::Single(w) => Self(FiniteNimberEnum::Single(w)), + FiniteNimberRef::Slice(v) => FiniteNimber::from(v), + } } } impl From for FiniteNimber { fn from(val: Word) -> FiniteNimber { - FiniteNimber(vec![val]) + Self(FiniteNimberEnum::Single(val)) } } impl From> for FiniteNimber { - fn from(val: Vec) -> FiniteNimber { - FiniteNimber(val) + fn from(mut vec: Vec) -> FiniteNimber { + match vec + .iter() + .enumerate() + .rev() + .find(|(_index, word)| **word != 0) + { + None => Self(FiniteNimberEnum::Single(0)), + Some((0, w)) => Self(FiniteNimberEnum::Single(*w)), + Some((pos, _)) => { + vec.truncate(pos + 1); + Self(FiniteNimberEnum::Vector(vec)) + } + } } } impl From<&[Word]> for FiniteNimber { - fn from(val: &[Word]) -> FiniteNimber { - FiniteNimber(val.into()) + fn from(slice: &[Word]) -> FiniteNimber { + match slice + .iter() + .enumerate() + .rev() + .find(|(_index, word)| **word != 0) + { + None => Self(FiniteNimberEnum::Single(0)), + Some((0, w)) => Self(FiniteNimberEnum::Single(*w)), + Some(_) => Self(FiniteNimberEnum::Vector(slice.into())) + } } } @@ -322,14 +374,13 @@ macro_rules! impl_binop_wrappers { impl<'a, 'b> Add> for FiniteNimberRef<'b> { type Output = FiniteNimber; fn add(self, other: FiniteNimberRef<'a>) -> FiniteNimber { - FiniteNimber( - self.as_slice() - .iter() - .cloned() - .zip_longest(other.as_slice().iter().cloned()) - .map(|pair| pair.reduce(|a, b| a ^ b)) - .collect(), - ) + let vec: Vec<_> = self.as_slice() + .iter() + .cloned() + .zip_longest(other.as_slice().iter().cloned()) + .map(|pair| pair.reduce(|a, b| a ^ b)) + .collect(); + FiniteNimber::from(vec) } }