This commit is contained in:
		
							parent
							
								
									14a9c23697
								
							
						
					
					
						commit
						be025c14ca
					
				
					 4 changed files with 285 additions and 263 deletions
				
			
		|  | @ -1,267 +1,20 @@ | |||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||
| // See Notices.txt for copyright information
 | ||||
| use crate::intern::{Intern, Interned}; | ||||
| use bitvec::{bits, order::Lsb0, slice::BitSlice, view::BitView}; | ||||
| use std::{ | ||||
|     cmp::Ordering, | ||||
|     fmt::{self, Debug, Write}, | ||||
|     hash::Hash, | ||||
|     mem::ManuallyDrop, | ||||
|     ptr, | ||||
|     rc::Rc, | ||||
|     sync::{Arc, OnceLock}, | ||||
| 
 | ||||
| mod const_bool; | ||||
| mod const_cmp; | ||||
| mod misc; | ||||
| 
 | ||||
| #[doc(inline)] | ||||
| pub use const_bool::{ConstBool, ConstBoolDispatch, ConstBoolDispatchTag, GenericConstBool}; | ||||
| 
 | ||||
| #[doc(inline)] | ||||
| pub use const_cmp::{ | ||||
|     const_bytes_cmp, const_str_array_is_strictly_ascending, const_str_cmp, const_u8_cmp, | ||||
|     const_usize_cmp, | ||||
| }; | ||||
| 
 | ||||
| mod sealed { | ||||
|     pub trait Sealed {} | ||||
| } | ||||
| 
 | ||||
| /// # Safety
 | ||||
| /// the only implementation is `ConstBool<Self::VALUE>`
 | ||||
| pub unsafe trait GenericConstBool: | ||||
|     sealed::Sealed + Copy + Ord + Hash + Default + Debug + 'static + Send + Sync | ||||
| { | ||||
|     const VALUE: bool; | ||||
| } | ||||
| 
 | ||||
| #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] | ||||
| pub struct ConstBool<const VALUE: bool>; | ||||
| 
 | ||||
| impl<const VALUE: bool> Debug for ConstBool<VALUE> { | ||||
|     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||
|         f.debug_tuple("ConstBool").field(&Self::VALUE).finish() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<const VALUE: bool> sealed::Sealed for ConstBool<VALUE> {} | ||||
| 
 | ||||
| /// SAFETY: the only implementation is `ConstBool<Self::VALUE>`
 | ||||
| unsafe impl<const VALUE: bool> GenericConstBool for ConstBool<VALUE> { | ||||
|     const VALUE: bool = VALUE; | ||||
| } | ||||
| 
 | ||||
| pub trait ConstBoolDispatchTag { | ||||
|     type Type<Select: GenericConstBool>; | ||||
| } | ||||
| 
 | ||||
| pub enum ConstBoolDispatch<FalseT, TrueT> { | ||||
|     False(FalseT), | ||||
|     True(TrueT), | ||||
| } | ||||
| 
 | ||||
| impl<FalseT, TrueT> ConstBoolDispatch<FalseT, TrueT> { | ||||
|     pub fn new< | ||||
|         Tag: ConstBoolDispatchTag<Type<ConstBool<false>> = FalseT> | ||||
|             + ConstBoolDispatchTag<Type<ConstBool<true>> = TrueT>, | ||||
|         Select: GenericConstBool, | ||||
|     >( | ||||
|         v: Tag::Type<Select>, | ||||
|     ) -> Self { | ||||
|         let v = ManuallyDrop::new(v); | ||||
|         let v_ptr: *const Tag::Type<Select> = &*v; | ||||
|         // SAFETY: reads the exact same type, since Select is really ConstBool<Select::VALUE>
 | ||||
|         unsafe { | ||||
|             if Select::VALUE { | ||||
|                 ConstBoolDispatch::True(ptr::read(v_ptr.cast())) | ||||
|             } else { | ||||
|                 ConstBoolDispatch::False(ptr::read(v_ptr.cast())) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// replacement for Iterator::eq_by which isn't stable yet
 | ||||
| pub fn iter_eq_by<L: IntoIterator, R: IntoIterator, F: FnMut(L::Item, R::Item) -> bool>( | ||||
|     l: L, | ||||
|     r: R, | ||||
|     mut f: F, | ||||
| ) -> bool { | ||||
|     let mut l = l.into_iter(); | ||||
|     let mut r = r.into_iter(); | ||||
|     l.try_for_each(|l| { | ||||
|         if let Some(r) = r.next() { | ||||
|             f(l, r).then_some(()) | ||||
|         } else { | ||||
|             None | ||||
|         } | ||||
|     }) | ||||
|     .is_some() | ||||
|         && r.next().is_none() | ||||
| } | ||||
| 
 | ||||
| pub struct DebugAsDisplay<T>(pub T); | ||||
| 
 | ||||
| impl<T: fmt::Display> fmt::Debug for DebugAsDisplay<T> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         self.0.fmt(f) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub const fn const_u8_cmp(a: u8, b: u8) -> Ordering { | ||||
|     if a < b { | ||||
|         Ordering::Less | ||||
|     } else if a > b { | ||||
|         Ordering::Greater | ||||
|     } else { | ||||
|         Ordering::Equal | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub const fn const_usize_cmp(a: usize, b: usize) -> Ordering { | ||||
|     if a < b { | ||||
|         Ordering::Less | ||||
|     } else if a > b { | ||||
|         Ordering::Greater | ||||
|     } else { | ||||
|         Ordering::Equal | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub const fn const_bytes_cmp(a: &[u8], b: &[u8]) -> Ordering { | ||||
|     let mut i = 0; | ||||
|     while i < a.len() && i < b.len() { | ||||
|         match const_u8_cmp(a[i], b[i]) { | ||||
|             Ordering::Equal => {} | ||||
|             retval => return retval, | ||||
|         } | ||||
|         i += 1; | ||||
|     } | ||||
|     const_usize_cmp(a.len(), b.len()) | ||||
| } | ||||
| 
 | ||||
| pub const fn const_str_cmp(a: &str, b: &str) -> Ordering { | ||||
|     const_bytes_cmp(a.as_bytes(), b.as_bytes()) | ||||
| } | ||||
| 
 | ||||
| pub const fn const_str_array_is_strictly_ascending(a: &[&str]) -> bool { | ||||
|     let mut i = 1; | ||||
|     while i < a.len() { | ||||
|         match const_str_cmp(a[i - 1], a[i]) { | ||||
|             Ordering::Less => {} | ||||
|             _ => return false, | ||||
|         } | ||||
|         i += 1; | ||||
|     } | ||||
|     true | ||||
| } | ||||
| 
 | ||||
| pub trait MakeMutSlice { | ||||
|     type Element: Clone; | ||||
|     fn make_mut_slice(&mut self) -> &mut [Self::Element]; | ||||
| } | ||||
| 
 | ||||
| impl<T: Clone> MakeMutSlice for Arc<[T]> { | ||||
|     type Element = T; | ||||
| 
 | ||||
|     fn make_mut_slice(&mut self) -> &mut [Self::Element] { | ||||
|         if Arc::get_mut(self).is_none() { | ||||
|             *self = Arc::<[T]>::from(&**self); | ||||
|         } | ||||
|         Arc::get_mut(self).unwrap() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: Clone> MakeMutSlice for Rc<[T]> { | ||||
|     type Element = T; | ||||
| 
 | ||||
|     fn make_mut_slice(&mut self) -> &mut [Self::Element] { | ||||
|         if Rc::get_mut(self).is_none() { | ||||
|             *self = Rc::<[T]>::from(&**self); | ||||
|         } | ||||
|         Rc::get_mut(self).unwrap() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct DebugAsRawString<'a>(pub &'a str); | ||||
| 
 | ||||
| impl fmt::Debug for DebugAsRawString<'_> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         let pounds = self | ||||
|             .0 | ||||
|             .split_inclusive('"') | ||||
|             .skip(1) | ||||
|             .map(|v| v.len() - v.trim_start_matches('#').len()) | ||||
|             .max() | ||||
|             .map(|v| v + 1) | ||||
|             .unwrap_or(0); | ||||
|         f.write_char('r')?; | ||||
|         for _ in 0..pounds { | ||||
|             f.write_char('#')?; | ||||
|         } | ||||
|         f.write_char('"')?; | ||||
|         f.write_str(self.0)?; | ||||
|         f.write_char('"')?; | ||||
|         for _ in 0..pounds { | ||||
|             f.write_char('#')?; | ||||
|         } | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub fn interned_bit(v: bool) -> Interned<BitSlice> { | ||||
|     static RETVAL: OnceLock<[Interned<BitSlice>; 2]> = OnceLock::new(); | ||||
|     // use bits![V; 1] instead of bits![V] to work around https://github.com/ferrilab/bitvec/issues/271
 | ||||
|     RETVAL.get_or_init(|| [bits![0; 1].intern(), bits![1; 1].intern()])[v as usize] | ||||
| } | ||||
| 
 | ||||
| #[derive(Copy, Clone, Debug)] | ||||
| pub struct BitSliceWriteWithBase<'a>(pub &'a BitSlice); | ||||
| 
 | ||||
| impl BitSliceWriteWithBase<'_> { | ||||
|     fn fmt_with_base<const BITS_PER_DIGIT: usize, const UPPER_CASE: bool>( | ||||
|         self, | ||||
|         f: &mut fmt::Formatter<'_>, | ||||
|     ) -> fmt::Result { | ||||
|         let digit_count = self.0.len().div_ceil(BITS_PER_DIGIT).max(1); | ||||
|         // TODO: optimize
 | ||||
|         let mut buf = String::with_capacity(digit_count); | ||||
|         for digit_index in (0..digit_count).rev() { | ||||
|             let mut digit = 0; | ||||
|             let src = self | ||||
|                 .0 | ||||
|                 .get(digit_index * BITS_PER_DIGIT..) | ||||
|                 .unwrap_or_default(); | ||||
|             let src = src.get(..BITS_PER_DIGIT).unwrap_or(src); | ||||
|             digit.view_bits_mut::<Lsb0>()[..src.len()].copy_from_bitslice(src); | ||||
|             let mut ch = char::from_digit(digit as u32, 1 << BITS_PER_DIGIT).unwrap(); | ||||
|             if UPPER_CASE { | ||||
|                 ch = ch.to_ascii_uppercase(); | ||||
|             } | ||||
|             buf.push(ch); | ||||
|         } | ||||
|         f.pad_integral( | ||||
|             true, | ||||
|             match BITS_PER_DIGIT { | ||||
|                 1 => "0b", | ||||
|                 3 => "0o", | ||||
|                 4 => "0x", | ||||
|                 _ => "", | ||||
|             }, | ||||
|             &buf, | ||||
|         ) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl fmt::Binary for BitSliceWriteWithBase<'_> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         self.fmt_with_base::<1, false>(f) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl fmt::Octal for BitSliceWriteWithBase<'_> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         self.fmt_with_base::<3, false>(f) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl fmt::LowerHex for BitSliceWriteWithBase<'_> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         self.fmt_with_base::<4, false>(f) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl fmt::UpperHex for BitSliceWriteWithBase<'_> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         self.fmt_with_base::<4, true>(f) | ||||
|     } | ||||
| } | ||||
| #[doc(inline)] | ||||
| pub use misc::{ | ||||
|     interned_bit, iter_eq_by, BitSliceWriteWithBase, DebugAsDisplay, DebugAsRawString, MakeMutSlice, | ||||
| }; | ||||
|  |  | |||
							
								
								
									
										61
									
								
								crates/fayalite/src/util/const_bool.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								crates/fayalite/src/util/const_bool.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,61 @@ | |||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||
| // See Notices.txt for copyright information
 | ||||
| use std::{fmt::Debug, hash::Hash, mem::ManuallyDrop, ptr}; | ||||
| 
 | ||||
| mod sealed { | ||||
|     pub trait Sealed {} | ||||
| } | ||||
| 
 | ||||
| /// # Safety
 | ||||
| /// the only implementation is `ConstBool<Self::VALUE>`
 | ||||
| pub unsafe trait GenericConstBool: | ||||
|     sealed::Sealed + Copy + Ord + Hash + Default + Debug + 'static + Send + Sync | ||||
| { | ||||
|     const VALUE: bool; | ||||
| } | ||||
| 
 | ||||
| #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] | ||||
| pub struct ConstBool<const VALUE: bool>; | ||||
| 
 | ||||
| impl<const VALUE: bool> Debug for ConstBool<VALUE> { | ||||
|     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||
|         f.debug_tuple("ConstBool").field(&Self::VALUE).finish() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<const VALUE: bool> sealed::Sealed for ConstBool<VALUE> {} | ||||
| 
 | ||||
| /// SAFETY: the only implementation is `ConstBool<Self::VALUE>`
 | ||||
| unsafe impl<const VALUE: bool> GenericConstBool for ConstBool<VALUE> { | ||||
|     const VALUE: bool = VALUE; | ||||
| } | ||||
| 
 | ||||
| pub trait ConstBoolDispatchTag { | ||||
|     type Type<Select: GenericConstBool>; | ||||
| } | ||||
| 
 | ||||
| pub enum ConstBoolDispatch<FalseT, TrueT> { | ||||
|     False(FalseT), | ||||
|     True(TrueT), | ||||
| } | ||||
| 
 | ||||
| impl<FalseT, TrueT> ConstBoolDispatch<FalseT, TrueT> { | ||||
|     pub fn new< | ||||
|         Tag: ConstBoolDispatchTag<Type<ConstBool<false>> = FalseT> | ||||
|             + ConstBoolDispatchTag<Type<ConstBool<true>> = TrueT>, | ||||
|         Select: GenericConstBool, | ||||
|     >( | ||||
|         v: Tag::Type<Select>, | ||||
|     ) -> Self { | ||||
|         let v = ManuallyDrop::new(v); | ||||
|         let v_ptr: *const Tag::Type<Select> = &*v; | ||||
|         // SAFETY: reads the exact same type, since Select is really ConstBool<Select::VALUE>
 | ||||
|         unsafe { | ||||
|             if Select::VALUE { | ||||
|                 ConstBoolDispatch::True(ptr::read(v_ptr.cast())) | ||||
|             } else { | ||||
|                 ConstBoolDispatch::False(ptr::read(v_ptr.cast())) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										51
									
								
								crates/fayalite/src/util/const_cmp.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								crates/fayalite/src/util/const_cmp.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,51 @@ | |||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||
| // See Notices.txt for copyright information
 | ||||
| use std::cmp::Ordering; | ||||
| 
 | ||||
| pub const fn const_u8_cmp(a: u8, b: u8) -> Ordering { | ||||
|     if a < b { | ||||
|         Ordering::Less | ||||
|     } else if a > b { | ||||
|         Ordering::Greater | ||||
|     } else { | ||||
|         Ordering::Equal | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub const fn const_usize_cmp(a: usize, b: usize) -> Ordering { | ||||
|     if a < b { | ||||
|         Ordering::Less | ||||
|     } else if a > b { | ||||
|         Ordering::Greater | ||||
|     } else { | ||||
|         Ordering::Equal | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub const fn const_bytes_cmp(a: &[u8], b: &[u8]) -> Ordering { | ||||
|     let mut i = 0; | ||||
|     while i < a.len() && i < b.len() { | ||||
|         match const_u8_cmp(a[i], b[i]) { | ||||
|             Ordering::Equal => {} | ||||
|             retval => return retval, | ||||
|         } | ||||
|         i += 1; | ||||
|     } | ||||
|     const_usize_cmp(a.len(), b.len()) | ||||
| } | ||||
| 
 | ||||
| pub const fn const_str_cmp(a: &str, b: &str) -> Ordering { | ||||
|     const_bytes_cmp(a.as_bytes(), b.as_bytes()) | ||||
| } | ||||
| 
 | ||||
| pub const fn const_str_array_is_strictly_ascending(a: &[&str]) -> bool { | ||||
|     let mut i = 1; | ||||
|     while i < a.len() { | ||||
|         match const_str_cmp(a[i - 1], a[i]) { | ||||
|             Ordering::Less => {} | ||||
|             _ => return false, | ||||
|         } | ||||
|         i += 1; | ||||
|     } | ||||
|     true | ||||
| } | ||||
							
								
								
									
										157
									
								
								crates/fayalite/src/util/misc.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								crates/fayalite/src/util/misc.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,157 @@ | |||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||
| // See Notices.txt for copyright information
 | ||||
| use crate::intern::{Intern, Interned}; | ||||
| use bitvec::{bits, order::Lsb0, slice::BitSlice, view::BitView}; | ||||
| use std::{ | ||||
|     fmt::{self, Debug, Write}, | ||||
|     rc::Rc, | ||||
|     sync::{Arc, OnceLock}, | ||||
| }; | ||||
| 
 | ||||
| /// replacement for Iterator::eq_by which isn't stable yet
 | ||||
| pub fn iter_eq_by<L: IntoIterator, R: IntoIterator, F: FnMut(L::Item, R::Item) -> bool>( | ||||
|     l: L, | ||||
|     r: R, | ||||
|     mut f: F, | ||||
| ) -> bool { | ||||
|     let mut l = l.into_iter(); | ||||
|     let mut r = r.into_iter(); | ||||
|     l.try_for_each(|l| { | ||||
|         if let Some(r) = r.next() { | ||||
|             f(l, r).then_some(()) | ||||
|         } else { | ||||
|             None | ||||
|         } | ||||
|     }) | ||||
|     .is_some() | ||||
|         && r.next().is_none() | ||||
| } | ||||
| 
 | ||||
| pub struct DebugAsDisplay<T>(pub T); | ||||
| 
 | ||||
| impl<T: fmt::Display> fmt::Debug for DebugAsDisplay<T> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         self.0.fmt(f) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub trait MakeMutSlice { | ||||
|     type Element: Clone; | ||||
|     fn make_mut_slice(&mut self) -> &mut [Self::Element]; | ||||
| } | ||||
| 
 | ||||
| impl<T: Clone> MakeMutSlice for Arc<[T]> { | ||||
|     type Element = T; | ||||
| 
 | ||||
|     fn make_mut_slice(&mut self) -> &mut [Self::Element] { | ||||
|         if Arc::get_mut(self).is_none() { | ||||
|             *self = Arc::<[T]>::from(&**self); | ||||
|         } | ||||
|         Arc::get_mut(self).unwrap() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: Clone> MakeMutSlice for Rc<[T]> { | ||||
|     type Element = T; | ||||
| 
 | ||||
|     fn make_mut_slice(&mut self) -> &mut [Self::Element] { | ||||
|         if Rc::get_mut(self).is_none() { | ||||
|             *self = Rc::<[T]>::from(&**self); | ||||
|         } | ||||
|         Rc::get_mut(self).unwrap() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct DebugAsRawString<'a>(pub &'a str); | ||||
| 
 | ||||
| impl fmt::Debug for DebugAsRawString<'_> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         let pounds = self | ||||
|             .0 | ||||
|             .split_inclusive('"') | ||||
|             .skip(1) | ||||
|             .map(|v| v.len() - v.trim_start_matches('#').len()) | ||||
|             .max() | ||||
|             .map(|v| v + 1) | ||||
|             .unwrap_or(0); | ||||
|         f.write_char('r')?; | ||||
|         for _ in 0..pounds { | ||||
|             f.write_char('#')?; | ||||
|         } | ||||
|         f.write_char('"')?; | ||||
|         f.write_str(self.0)?; | ||||
|         f.write_char('"')?; | ||||
|         for _ in 0..pounds { | ||||
|             f.write_char('#')?; | ||||
|         } | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub fn interned_bit(v: bool) -> Interned<BitSlice> { | ||||
|     static RETVAL: OnceLock<[Interned<BitSlice>; 2]> = OnceLock::new(); | ||||
|     // use bits![V; 1] instead of bits![V] to work around https://github.com/ferrilab/bitvec/issues/271
 | ||||
|     RETVAL.get_or_init(|| [bits![0; 1].intern(), bits![1; 1].intern()])[v as usize] | ||||
| } | ||||
| 
 | ||||
| #[derive(Copy, Clone, Debug)] | ||||
| pub struct BitSliceWriteWithBase<'a>(pub &'a BitSlice); | ||||
| 
 | ||||
| impl BitSliceWriteWithBase<'_> { | ||||
|     fn fmt_with_base<const BITS_PER_DIGIT: usize, const UPPER_CASE: bool>( | ||||
|         self, | ||||
|         f: &mut fmt::Formatter<'_>, | ||||
|     ) -> fmt::Result { | ||||
|         let digit_count = self.0.len().div_ceil(BITS_PER_DIGIT).max(1); | ||||
|         // TODO: optimize
 | ||||
|         let mut buf = String::with_capacity(digit_count); | ||||
|         for digit_index in (0..digit_count).rev() { | ||||
|             let mut digit = 0; | ||||
|             let src = self | ||||
|                 .0 | ||||
|                 .get(digit_index * BITS_PER_DIGIT..) | ||||
|                 .unwrap_or_default(); | ||||
|             let src = src.get(..BITS_PER_DIGIT).unwrap_or(src); | ||||
|             digit.view_bits_mut::<Lsb0>()[..src.len()].copy_from_bitslice(src); | ||||
|             let mut ch = char::from_digit(digit as u32, 1 << BITS_PER_DIGIT).unwrap(); | ||||
|             if UPPER_CASE { | ||||
|                 ch = ch.to_ascii_uppercase(); | ||||
|             } | ||||
|             buf.push(ch); | ||||
|         } | ||||
|         f.pad_integral( | ||||
|             true, | ||||
|             match BITS_PER_DIGIT { | ||||
|                 1 => "0b", | ||||
|                 3 => "0o", | ||||
|                 4 => "0x", | ||||
|                 _ => "", | ||||
|             }, | ||||
|             &buf, | ||||
|         ) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl fmt::Binary for BitSliceWriteWithBase<'_> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         self.fmt_with_base::<1, false>(f) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl fmt::Octal for BitSliceWriteWithBase<'_> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         self.fmt_with_base::<3, false>(f) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl fmt::LowerHex for BitSliceWriteWithBase<'_> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         self.fmt_with_base::<4, false>(f) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl fmt::UpperHex for BitSliceWriteWithBase<'_> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         self.fmt_with_base::<4, true>(f) | ||||
|     } | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue