Skip to content

Commit

Permalink
refactor: replace + with saturating_add where possible
Browse files Browse the repository at this point in the history
  • Loading branch information
SKalt committed Jan 26, 2025
1 parent 06682bf commit 7dcc05b
Show file tree
Hide file tree
Showing 6 changed files with 45 additions and 27 deletions.
14 changes: 9 additions & 5 deletions src/digest/encoded.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ impl<'src> EncodedSpan<'src> {
if len >= MAX_LEN {
return Error::at(len, EncodingTooLong).into();
}
len += 1; // safe since len < MAX_LEN < u16::MAX
len = len.saturating_add(1); // safe since len < MAX_LEN < u16::MAX
}

debug_assert!(len as usize == src.len(), "must have consume all src");
Expand Down Expand Up @@ -90,14 +90,18 @@ impl<'src> Encoded<'src> {
pub(crate) fn from_span(src: &'src str, span: EncodedSpan<'src>) -> Self {
Self(span.span_of(src))
}
#[allow(clippy::unwrap_used)]
/// validates whether every ascii character is a lowercase hex digit
fn is_lower_hex(&self) -> Result<(), Error> {
fn validate_all_lower_hex(&self) -> Result<(), Error> {
self.to_str().bytes().enumerate().try_for_each(|(i, c)| {
if matches!(c, b'a'..=b'f' | b'0'..=b'9') {
Ok(())
} else {
Error::at(i.try_into().unwrap(), OciRegisteredDigestInvalidChar).into()
#[allow(clippy::cast_possible_truncation)]
Error::at(
i as u16, // safe since i is at most 1024
OciRegisteredDigestInvalidChar,
)
.into()
}
})
}
Expand All @@ -106,7 +110,7 @@ impl<'src> Encoded<'src> {
fn validate_registered_algorithms(&self, algorithm: &Algorithm<'src>) -> Result<(), Error> {
match algorithm.to_str() {
"sha256" | "sha512" => {
self.is_lower_hex()?;
self.validate_all_lower_hex()?;
#[allow(clippy::cast_possible_truncation)]
match (algorithm.to_str(), self.len()) {
("sha256", 64) => Ok(()),
Expand Down
2 changes: 1 addition & 1 deletion src/digest/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ impl<'src> Digest<'src> {
/// The encoded digest value.
pub fn encoded(&self) -> Encoded<'src> {
Encoded::from_span(
&self.src[self.span.algorithm.len() + 1..],
&self.src[self.span.algorithm.len().saturating_add(1)..],
self.span.encoded,
)
}
Expand Down
6 changes: 5 additions & 1 deletion src/err.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,11 @@ where
type Output = Self;
#[allow(clippy::arithmetic_side_effects)] // FIXME: check for overflow
fn add(self, rhs: Int) -> Self {
Self(self.0 + rhs.into(), self.1)
#[cfg(debug_assertions)]
let len = self.0 + rhs.into();
#[cfg(not(debug_assertions))]
let len = self.0.saturating_add(rhs.into());
Self(len, self.1)
}
}

Expand Down
36 changes: 20 additions & 16 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ impl<'src> RefSpan<'src> {
},
DomainOrRefSpan::Domain(_) => {
index = index.saturating_add(1); // consume the leading slash; ok since index <= 256
let rest = &src[prefix.len() + 1..];
let rest = &src[prefix.len().saturating_add(1)..];
PathSpan::new(rest)
}
}
Expand All @@ -112,15 +112,15 @@ impl<'src> RefSpan<'src> {
DomainOrRefSpan::TaggedRef((_, right)) => match right {
Some(tag) => Ok(Some(tag)),
None => match src.as_bytes().get(index as usize) {
Some(b':') => TagSpan::new(&src[index as usize + 1..])
Some(b':') => TagSpan::new(&src[(index as usize).saturating_add(1)..])
.map_err(|e| e.into())
.map(Some),
Some(b'@') | None => Ok(None),
Some(_) => Error::at(0, err::Kind::PathInvalidChar).into(),
},
},
DomainOrRefSpan::Domain(_) => match src.as_bytes().get(index as usize) {
Some(b':') => TagSpan::new(&src[index as usize + 1..])
Some(b':') => TagSpan::new(&src[(index as usize).saturating_add(1)..])
.map(Some)
.map_err(|e| {
Error::at(
Expand Down Expand Up @@ -167,45 +167,49 @@ impl<'src> RefSpan<'src> {

/// the offset at which the path starts.
fn path_index(&self) -> usize {
self.name.domain.map(|d| d.len() + 1).unwrap_or(0)
self.name
.domain
.map(|d| d.len().saturating_add(1))
.unwrap_or(0)
}
/// the at which the tag starts. If a tag is present, `tag_index` is AFTER the leading ':'.
fn tag_index(&self) -> usize {
self.path_index()
+ self.name.path.len()
+ self.tag.map(|_| 1) // +1 for the leading ':'
.unwrap_or(0)
.saturating_add(self.name.path.len())
.saturating_add(self.tag.map(|_| 1).unwrap_or(0)) // +1 for the leading ':'
}
fn digest_index(&self) -> usize {
self.tag_index() // tag_index() accounts for the leading ':'
+ self.tag.map(|t| t.len()).unwrap_or(0)
+ self.digest.map(|_| 1) // 1 == consume the leading '@' if a digest is present
.unwrap_or(0)
.saturating_add(self.tag.map(|t| t.len()).unwrap_or(0))
.saturating_add(self.digest.map(|_| 1).unwrap_or(0)) // 1 == consume the leading '@' if a digest is present
}

fn port_range(&self) -> Option<Range<usize>> {
let domain = self.name.domain?;
let port = domain.port?;
let start = domain.host.len() + 1; // +1 to consume the ':'
Some(start..start + port.len())
let start = domain.host.len().saturating_add(1); // +1 to consume the ':'
let end = start.saturating_add(port.len());
Some(start..end)
}
fn path_range(&self) -> Range<usize> {
self.name
.domain
.map(|d| {
let start = d.len() + 1; // +1 to consume the leading '/'
start..(start + (self.name.path.len()))
let start = d.len().saturating_add(1); // +1 to consume the leading '/'
let end = start.saturating_add(self.name.path.len());
start..end
})
.unwrap_or(0..self.name.path.len())
}
fn name_range(&self) -> Range<usize> {
let end = self.path_index() + self.name.path.len();
let end = self.path_index().saturating_add(self.name.path.len());
0..end
}
fn tag_range(&self) -> Option<Range<usize>> {
let tag = self.tag?;
let start = self.tag_index();
Some(start..(start + tag.len()))
let end = start.saturating_add(tag.len());
Some(start..end)
}
fn digest_range(&self) -> Option<RangeFrom<usize>> {
self.digest.map(|_| self.digest_index()..)
Expand Down
7 changes: 4 additions & 3 deletions src/name/domain/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ impl<'src> DomainSpan<'src> {
let host = HostSpan::new(src)?;
let len: u16 = host.short_len().widen().into(); // max 255 chars
let port = match src.as_bytes().get(host.len()) {
Some(b':') => PortSpan::new(&src[host.len() + 1..])
Some(b':') => PortSpan::new(&src[host.len().saturating_add(1)..])
.map(Some)
.map_err(|e| Error::at(len.saturating_add(e.index().into()), e.kind())),
Some(b'/' | b'@') | None => Ok(None),
Expand Down Expand Up @@ -165,8 +165,9 @@ impl<'src> Domain<'src> {
/// Not including any leading `:`.
pub fn port(&self) -> Option<&str> {
let port = self.span.port?;
let start = self.span.host.len() + 1; // +1 for the leading ':'
let result = self.src.get(start..start + port.len());
let start = self.span.host.len().saturating_add(1); // +1 for the leading ':'
let end = start.saturating_add(port.len());
let result = self.src.get(start..end);
debug_assert!(
result.is_some(),
"{src:?}[{start}..{end}] is None",
Expand Down
7 changes: 6 additions & 1 deletion src/name/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,12 @@ impl<'src> Name<'src> {
}
/// Returns the path part of the name, which always exists.
pub fn path(&self) -> path::Path<'_> {
let src = &self.src[self.span.domain.map(|d| d.len() + 1).unwrap_or(0)..];
let start = self
.span
.domain
.map(|d| d.len().saturating_add(1))
.unwrap_or(0);
let src = &self.src[start..];
path::Path::from_span(self.span.path, src)
}
#[allow(missing_docs)]
Expand Down

0 comments on commit 7dcc05b

Please sign in to comment.