v0: introduce `HexNibbles` abstraction for leaf const parsing.
diff --git a/src/v0.rs b/src/v0.rs
index 24b5373..fa524be 100644
--- a/src/v0.rs
+++ b/src/v0.rs
@@ -1,3 +1,4 @@
+use core::convert::TryFrom;
use core::{char, fmt, mem};
#[allow(unused_macros)]
@@ -264,6 +265,30 @@
}
}
+/// Sequence of lowercase hexadecimal nibbles (`0-9a-f`), used by leaf consts.
+struct HexNibbles<'s> {
+ nibbles: &'s str,
+}
+
+impl<'s> HexNibbles<'s> {
+ /// Decode an integer value (with the "most significant nibble" first),
+ /// returning `None` if it can't fit in an `u64`.
+ // FIXME(eddyb) should this "just" use `u128` instead?
+ fn try_parse_uint(&self) -> Option<u64> {
+ let nibbles = self.nibbles.trim_start_matches("0");
+
+ if nibbles.len() > 16 {
+ return None;
+ }
+
+ let mut v = 0;
+ for nibble in nibbles.chars() {
+ v = (v << 4) | (nibble.to_digit(16).unwrap() as u64);
+ }
+ Some(v)
+ }
+}
+
fn basic_type(tag: u8) -> Option<&'static str> {
Some(match tag {
b'b' => "bool",
@@ -331,7 +356,7 @@
Ok(b)
}
- fn hex_nibbles(&mut self) -> Result<&'s str, ParseError> {
+ fn hex_nibbles(&mut self) -> Result<HexNibbles<'s>, ParseError> {
let start = self.next;
loop {
match self.next()? {
@@ -340,7 +365,9 @@
_ => return Err(ParseError::Invalid),
}
}
- Ok(&self.sym[start..self.next - 1])
+ Ok(HexNibbles {
+ nibbles: &self.sym[start..self.next - 1],
+ })
}
fn digit_10(&mut self) -> Result<u8, ParseError> {
@@ -976,16 +1003,14 @@
fn print_const_uint(&mut self, ty_tag: u8) -> fmt::Result {
let hex = parse!(self, hex_nibbles);
- // Print anything that doesn't fit in `u64` verbatim.
- if hex.len() > 16 {
- self.print("0x")?;
- self.print(hex)?;
- } else {
- let mut v = 0;
- for c in hex.chars() {
- v = (v << 4) | (c.to_digit(16).unwrap() as u64);
+ match hex.try_parse_uint() {
+ Some(v) => self.print(v)?,
+
+ // Print anything that doesn't fit in `u64` verbatim.
+ None => {
+ self.print("0x")?;
+ self.print(hex.nibbles)?;
}
- self.print(v)?;
}
if let Some(out) = &mut self.out {
@@ -1007,33 +1032,27 @@
}
fn print_const_bool(&mut self) -> fmt::Result {
- match parse!(self, hex_nibbles).as_bytes() {
- b"0" => self.print("false"),
- b"1" => self.print("true"),
+ match parse!(self, hex_nibbles).try_parse_uint() {
+ Some(0) => self.print("false"),
+ Some(1) => self.print("true"),
_ => invalid!(self),
}
}
fn print_const_char(&mut self) -> fmt::Result {
- let hex = parse!(self, hex_nibbles);
-
- // Valid `char`s fit in `u32`.
- if hex.len() > 8 {
- invalid!(self);
- }
-
- let mut v = 0;
- for c in hex.chars() {
- v = (v << 4) | (c.to_digit(16).unwrap() as u32);
- }
- if let Some(c) = char::from_u32(v) {
- if let Some(out) = &mut self.out {
- fmt::Debug::fmt(&c, out)?;
+ match parse!(self, hex_nibbles)
+ .try_parse_uint()
+ .and_then(|v| u32::try_from(v).ok())
+ .and_then(char::from_u32)
+ {
+ Some(c) => {
+ if let Some(out) = &mut self.out {
+ fmt::Debug::fmt(&c, out)?;
+ }
+ Ok(())
}
- } else {
- invalid!(self);
+ None => invalid!(self),
}
- Ok(())
}
}