Skip to content

Commit

Permalink
Don't throw away people's data randomly
Browse files Browse the repository at this point in the history
  • Loading branch information
GnomedDev committed Jan 13, 2024
1 parent a7e4d9f commit 37e97bf
Show file tree
Hide file tree
Showing 8 changed files with 81 additions and 131 deletions.
67 changes: 8 additions & 59 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 1 addition & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,13 @@ edition = "2021"
license = "MIT"

[dependencies]
log = { version = "0.4.20", optional = true }
nonmax = "0.5.5"
nonmax = "0.5"
serde = { version = "1.0.193", optional = true }
tracing = { version = "0.1.40", optional = true }
typesize = { version = "0.1.3", optional = true, features = ["nonmax"] }

[features]
typesize = ["dep:typesize"]
serde = ["dep:serde"]
log_using_tracing = ["dep:tracing"]
log_using_log = ["dep:log"]

[package.metadata.docs.rs]
all-features = true
Expand Down
56 changes: 18 additions & 38 deletions src/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@ use std::{borrow::Cow, fmt::Debug, hash::Hash, mem::ManuallyDrop, ptr::NonNull};

use crate::length::{InvalidLength, SmallLen, ValidLength};

#[cold]
fn truncate_vec<T>(err: InvalidLength<T>, max_len: usize) -> Vec<T> {
let mut value = Vec::from(err.get_inner());
value.truncate(max_len);
value
}

/// A fixed size array with length provided at creation denoted in a [`ValidLength`], by default [`u32`].
///
/// See module level documentation for more information.
Expand Down Expand Up @@ -40,6 +47,15 @@ impl<T, LenT: ValidLength> FixedArray<T, LenT> {
}
}

/// Converts [`Vec<T>`] into [`FixedArray<T>`] while truncating the vector if above the maximum size of `LenT`.
#[must_use]
pub fn from_vector_trunc(vec: Vec<T>) -> Self {
match vec.into_boxed_slice().try_into() {
Ok(v) => v,
Err(err) => Self::from_vector_trunc(truncate_vec(err, LenT::MAX)),
}
}

pub(crate) fn small_len(&self) -> LenT {
self.len
}
Expand Down Expand Up @@ -195,13 +211,6 @@ impl<'a, T, LenT: ValidLength> IntoIterator for &'a mut FixedArray<T, LenT> {
}
}

#[cfg(any(feature = "log_using_log", feature = "log_using_tracing"))]
impl<T, LenT: ValidLength> std::iter::FromIterator<T> for FixedArray<T, LenT> {
fn from_iter<Iter: IntoIterator<Item = T>>(iter: Iter) -> Self {
Vec::from_iter(iter).into()
}
}

impl<T, LenT: ValidLength> From<FixedArray<T, LenT>> for Box<[T]> {
fn from(value: FixedArray<T, LenT>) -> Self {
let mut value = ManuallyDrop::new(value);
Expand Down Expand Up @@ -238,32 +247,6 @@ impl<T, LenT: ValidLength> TryFrom<Box<[T]>> for FixedArray<T, LenT> {
}
}

#[inline(never)]
#[cfg(any(feature = "log_using_log", feature = "log_using_tracing"))]
fn log_truncation(backtrace: &std::backtrace::Backtrace, len: usize) {
crate::logging::error!("Truncating Vec<T> to fit into max len {len}\n{backtrace}");
}

#[cold]
#[cfg(any(feature = "log_using_log", feature = "log_using_tracing"))]
fn truncate_vec<T>(err: InvalidLength<T>, max_len: usize) -> Vec<T> {
log_truncation(&err.backtrace, max_len);

let mut value = Vec::from(err.get_inner());
value.truncate(max_len);
value
}

#[cfg(any(feature = "log_using_log", feature = "log_using_tracing"))]
impl<T, LenT: ValidLength> From<Vec<T>> for FixedArray<T, LenT> {
fn from(value: Vec<T>) -> Self {
match value.into_boxed_slice().try_into() {
Ok(arr) => arr,
Err(err) => Self::from(truncate_vec(err, LenT::MAX)),
}
}
}

impl<T, LenT: ValidLength> AsRef<[T]> for FixedArray<T, LenT> {
fn as_ref(&self) -> &[T] {
self
Expand All @@ -276,17 +259,14 @@ impl<T, LenT: ValidLength> From<FixedArray<T, LenT>> for std::sync::Arc<[T]> {
}
}

#[cfg(all(
feature = "serde",
any(feature = "log_using_log", feature = "log_using_tracing")
))]
#[cfg(feature = "serde")]
impl<'de, T, LenT> serde::Deserialize<'de> for FixedArray<T, LenT>
where
T: serde::Deserialize<'de>,
LenT: ValidLength,
{
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
Vec::<T>::deserialize(deserializer).map(Self::from)
Self::try_from(Box::<[T]>::deserialize(deserializer)?).map_err(serde::de::Error::custom)
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/length.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,8 @@ pub trait ValidLength: sealed::Sealed + Default + Copy + TryFrom<usize> + Into<u
fn to_usize(self) -> usize;

#[must_use]
fn from_usize(len: usize) -> Option<Self> {
len.try_into().ok()
fn from_usize(val: usize) -> Option<Self> {
val.try_into().ok()
}
}

Expand Down
8 changes: 2 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,17 @@
//! ## Features
//! - `serde`: Provides [`serde`] implementations for [`FixedArray`] and [`FixedString`].
//! - `typesize`: Provides [`typesize`] implementations for [`FixedArray`] and [`FixedString`].
//!
//! ## From implementations
//! [`From<Vec<T>>`]` for `[`FixedArray`] and [`From<String>`]` for `[`FixedString`] are only implemented if one of
//! `log_using_log` or `log_using_tracing` are enabled, as the implementations will `error` level log
//! if the Vec/String's length is too high for the provided `LenT` generic.
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
#![warn(clippy::pedantic, clippy::as_conversions)]
#![allow(clippy::module_name_repetitions)]

mod array;
mod inline;
mod length;
mod logging;
mod string;
mod truncating_into;

pub use array::FixedArray;
pub use length::ValidLength;
pub use string::FixedString;
pub use truncating_into::TruncatingInto;
4 changes: 0 additions & 4 deletions src/logging.rs

This file was deleted.

40 changes: 23 additions & 17 deletions src/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,19 @@ enum FixedStringRepr<LenT: ValidLength> {
Inline(InlineString<LenT::InlineStrRepr>),
}

#[cold]
fn truncate_string(err: InvalidStrLength, max_len: usize) -> String {
let mut value = String::from(err.get_inner());
for len in max_len..0 {
if value.is_char_boundary(len) {
value.truncate(len);
return value;
}
}

unreachable!("Len 0 is a char boundary")
}

/// A fixed size String with length provided at creation denoted in [`ValidLength`], by default [`u32`].
///
/// See module level documentation for more information.
Expand All @@ -24,6 +37,14 @@ impl<LenT: ValidLength> FixedString<LenT> {
FixedString(FixedStringRepr::Inline(InlineString::from_str("")))
}

#[must_use]
pub fn from_string_trunc(str: String) -> Self {
match str.into_boxed_str().try_into() {
Ok(val) => val,
Err(err) => Self::from_string_trunc(truncate_string(err, LenT::MAX)),
}
}

/// Returns the length of the [`FixedString`].
#[must_use]
pub fn len(&self) -> u32 {
Expand Down Expand Up @@ -183,18 +204,6 @@ impl<LenT: ValidLength> TryFrom<Box<str>> for FixedString<LenT> {
}
}

#[cfg(any(feature = "log_using_log", feature = "log_using_tracing"))]
impl<LenT: ValidLength> From<String> for FixedString<LenT> {
fn from(value: String) -> Self {
if value.len() > get_heap_threshold::<LenT>() {
let value = value.into_bytes().into();
Self(FixedStringRepr::Heap(value))
} else {
Self(FixedStringRepr::Inline(InlineString::from_str(&value)))
}
}
}

impl<LenT: ValidLength> From<FixedString<LenT>> for String {
fn from(value: FixedString<LenT>) -> Self {
match value.0 {
Expand Down Expand Up @@ -235,13 +244,10 @@ impl<LenT: ValidLength> From<FixedString<LenT>> for std::sync::Arc<str> {
}
}

#[cfg(all(
feature = "serde",
any(feature = "log_using_log", feature = "log_using_tracing")
))]
#[cfg(feature = "serde")]
impl<'de, LenT: ValidLength> serde::Deserialize<'de> for FixedString<LenT> {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
String::deserialize(deserializer).map(Self::from)
Self::try_from(Box::<str>::deserialize(deserializer)?).map_err(serde::de::Error::custom)
}
}

Expand Down
27 changes: 27 additions & 0 deletions src/truncating_into.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use crate::{FixedArray, FixedString, ValidLength};
mod sealed {
pub trait Sealed {}

impl Sealed for String {}
impl<T> Sealed for Vec<T> {}
}

/// A sealed helper trait for calling [`FixedArray<T>::from_vector_trunc`] or [`FixedString::from_string_trunc`].
///
/// Both of these functions may truncate the input in order to fit it into the provided [`ValidLength`],
/// therefore this trait must be imported in order to make possible truncation made obvious in user code.
pub trait TruncatingInto<T>: sealed::Sealed {
fn trunc_into(self) -> T;
}

impl<LenT: ValidLength> TruncatingInto<FixedString<LenT>> for String {
fn trunc_into(self) -> FixedString<LenT> {
FixedString::from_string_trunc(self)
}
}

impl<T, LenT: ValidLength> TruncatingInto<FixedArray<T, LenT>> for Vec<T> {
fn trunc_into(self) -> FixedArray<T, LenT> {
FixedArray::from_vector_trunc(self)
}
}

0 comments on commit 37e97bf

Please sign in to comment.