diff --git a/Cargo.toml b/Cargo.toml index da95c1c..57d480c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ categories = ["memory-management", "data-structures"] license = "MIT OR Apache-2.0" edition = "2021" readme = "README.md" -rust-version = "1.81.0" +rust-version = "1.83.0" [package.metadata.docs.rs] all-features = true diff --git a/src/bytes.rs b/src/bytes.rs index 481809e..c7be7b4 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -71,6 +71,50 @@ where Self::inline_empty() } + /// Creates a new inline `HipByt` by copying the given slice. + /// The slice **must not** be too large to be inlined. + /// + /// # Panics + /// + /// It panics if the slice is too large. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use hipstr::HipByt; + /// let s = HipByt::inline(b"hello\0"); + /// assert_eq!(s, b"hello\0"); + /// ``` + pub const fn inline(bytes: &[u8]) -> Self { + assert!(bytes.len() <= Self::inline_capacity(), "slice too large"); + + // SAFETY: length checked above + unsafe { Self::inline_unchecked(bytes) } + } + + /// Creates a new inline `HipByt` by copying the given the slice. + /// Return `None` if the given slice is too large to be inlined. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use hipstr::HipByt; + /// let s = HipByt::try_inline(b"hello\0").unwrap(); + /// assert_eq!(s, b"hello\0"); + /// ``` + pub const fn try_inline(bytes: &[u8]) -> Option { + if bytes.len() <= Self::inline_capacity() { + // SAFETY: length checked above + Some(unsafe { Self::inline_unchecked(bytes) }) + } else { + None + } + } + /// Creates a new `HipByt` with the given capacity. /// /// The underlying representation is not **normalized**. diff --git a/src/bytes/raw/inline.rs b/src/bytes/raw/inline.rs index 7faa35f..13dfa4b 100644 --- a/src/bytes/raw/inline.rs +++ b/src/bytes/raw/inline.rs @@ -106,11 +106,13 @@ impl Inline { /// Creates a new `Inline` string by copying a byte slice. #[inline] #[cfg(test)] - fn new(sl: &[u8]) -> Self { - assert!(sl.len() <= INLINE_CAPACITY); - - // SAFETY: length check above - unsafe { Self::new_unchecked(sl) } + const fn new(sl: &[u8]) -> Option { + if sl.len() <= INLINE_CAPACITY { + // SAFETY: length check above + Some(unsafe { Self::new_unchecked(sl) }) + } else { + None + } } /// Creates a new `Inline` string by copying a short byte slice. @@ -118,7 +120,7 @@ impl Inline { /// # Safety /// /// The input slice's length MUST be at most `INLINE_CAPACITY`. - pub unsafe fn new_unchecked(sl: &[u8]) -> Self { + pub const unsafe fn new_unchecked(sl: &[u8]) -> Self { let len = sl.len(); debug_assert!(len <= INLINE_CAPACITY); @@ -167,9 +169,7 @@ impl Inline { /// Returns a mutable view of this inline string. #[inline] - pub fn as_mut_slice(&mut self) -> &mut [u8] { - // XXX should add const: waiting for const_mut_refs - + pub const fn as_mut_slice(&mut self) -> &mut [u8] { debug_assert!(self.is_valid()); // HACK could be done without less unsafe: maybe_uninit_slice diff --git a/src/bytes/raw/inline/tests.rs b/src/bytes/raw/inline/tests.rs index 3e55a66..e9bdb66 100644 --- a/src/bytes/raw/inline/tests.rs +++ b/src/bytes/raw/inline/tests.rs @@ -6,7 +6,7 @@ type I = Inline; #[test] fn test_clone() { - let a: I = Inline::new(b"abc"); + let a: I = Inline::new(b"abc").unwrap(); let b = a.clone(); assert_eq!(a.as_slice(), b.as_slice()); }