From 6bc78bf523fa5f15cd4a001ce724c20e77eeec5c Mon Sep 17 00:00:00 2001 From: CrazyboyQCD <53971641+CrazyboyQCD@users.noreply.github.com> Date: Tue, 9 Jul 2024 20:16:46 +0800 Subject: [PATCH] Add `get_unchecked` method to `JsString` and `JsStr` (#3898) * feat: add `get_unchecked` method to `JsString` and `JsStr` * Clippy: fix * chore: apply docs suggestions --- core/string/src/lib.rs | 15 +++++++ core/string/src/str.rs | 95 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+) diff --git a/core/string/src/lib.rs b/core/string/src/lib.rs index 2daae7d5c6..01a571cbab 100644 --- a/core/string/src/lib.rs +++ b/core/string/src/lib.rs @@ -823,6 +823,21 @@ impl JsString { I::get(self.as_str(), index) } + /// Returns an element or subslice depending on the type of index, without doing bounds check. + /// + /// # Safety + /// + /// Caller must ensure the index is not out of bounds + #[inline] + #[must_use] + pub unsafe fn get_unchecked<'a, I>(&'a self, index: I) -> I::Value + where + I: JsSliceIndex<'a>, + { + // Safety: Caller must ensure the index is not out of bounds + unsafe { I::get_unchecked(self.as_str(), index) } + } + /// Get the element a the given index. /// /// # Panics diff --git a/core/string/src/str.rs b/core/string/src/str.rs index ef31ac7a27..5a1f24efd3 100644 --- a/core/string/src/str.rs +++ b/core/string/src/str.rs @@ -191,6 +191,21 @@ impl<'a> JsStr<'a> { I::get(self, index) } + /// Returns an element or subslice depending on the type of index, without doing bounds check. + /// + /// # Safety + /// + /// Caller must ensure the index is not out of bounds + #[inline] + #[must_use] + pub unsafe fn get_unchecked(self, index: I) -> I::Value + where + I: JsSliceIndex<'a>, + { + // Safety: Caller must ensure the index is not out of bounds + unsafe { I::get_unchecked(self, index) } + } + /// Convert the [`JsStr`] into a [`Vec`]. #[inline] #[must_use] @@ -295,6 +310,8 @@ pub trait JsSliceIndex<'a>: SliceIndex<[u8]> + SliceIndex<[u16]> { type Value; fn get(_: JsStr<'a>, index: Self) -> Option; + + unsafe fn get_unchecked(value: JsStr<'a>, index: Self) -> Self::Value; } impl<'a> JsSliceIndex<'a> for usize { @@ -307,6 +324,20 @@ impl<'a> JsSliceIndex<'a> for usize { JsStrVariant::Utf16(v) => v.get(index).copied(), } } + + /// # Safety + /// + /// Caller must ensure the index is not out of bounds + #[inline] + unsafe fn get_unchecked(value: JsStr<'a>, index: Self) -> Self::Value { + // Safety: Caller must ensure the index is not out of bounds + unsafe { + match value.variant() { + JsStrVariant::Latin1(v) => u16::from(*v.get_unchecked(index)), + JsStrVariant::Utf16(v) => *v.get_unchecked(index), + } + } + } } impl<'a> JsSliceIndex<'a> for std::ops::Range { @@ -319,6 +350,20 @@ impl<'a> JsSliceIndex<'a> for std::ops::Range { JsStrVariant::Utf16(v) => v.get(index).map(JsStr::utf16), } } + + /// # Safety + /// + /// Caller must ensure the index is not out of bounds + #[inline] + unsafe fn get_unchecked(value: JsStr<'a>, index: Self) -> Self::Value { + // Safety: Caller must ensure the index is not out of bounds + unsafe { + match value.variant() { + JsStrVariant::Latin1(v) => JsStr::latin1(v.get_unchecked(index)), + JsStrVariant::Utf16(v) => JsStr::utf16(v.get_unchecked(index)), + } + } + } } impl<'a> JsSliceIndex<'a> for std::ops::RangeInclusive { @@ -331,6 +376,20 @@ impl<'a> JsSliceIndex<'a> for std::ops::RangeInclusive { JsStrVariant::Utf16(v) => v.get(index).map(JsStr::utf16), } } + + /// # Safety + /// + /// Caller must ensure the index is not out of bounds + #[inline] + unsafe fn get_unchecked(value: JsStr<'a>, index: Self) -> Self::Value { + // Safety: Caller must ensure the index is not out of bounds + unsafe { + match value.variant() { + JsStrVariant::Latin1(v) => JsStr::latin1(v.get_unchecked(index)), + JsStrVariant::Utf16(v) => JsStr::utf16(v.get_unchecked(index)), + } + } + } } impl<'a> JsSliceIndex<'a> for std::ops::RangeFrom { @@ -343,6 +402,20 @@ impl<'a> JsSliceIndex<'a> for std::ops::RangeFrom { JsStrVariant::Utf16(v) => v.get(index).map(JsStr::utf16), } } + + /// # Safety + /// + /// Caller must ensure the index is not out of bounds + #[inline] + unsafe fn get_unchecked(value: JsStr<'a>, index: Self) -> Self::Value { + // Safety: Caller must ensure the index is not out of bounds + unsafe { + match value.variant() { + JsStrVariant::Latin1(v) => JsStr::latin1(v.get_unchecked(index)), + JsStrVariant::Utf16(v) => JsStr::utf16(v.get_unchecked(index)), + } + } + } } impl<'a> JsSliceIndex<'a> for std::ops::RangeTo { @@ -355,6 +428,20 @@ impl<'a> JsSliceIndex<'a> for std::ops::RangeTo { JsStrVariant::Utf16(v) => v.get(index).map(JsStr::utf16), } } + + /// # Safety + /// + /// Caller must ensure the index is not out of bounds + #[inline] + unsafe fn get_unchecked(value: JsStr<'a>, index: Self) -> Self::Value { + // Safety: Caller must ensure the index is not out of bounds + unsafe { + match value.variant() { + JsStrVariant::Latin1(v) => JsStr::latin1(v.get_unchecked(index)), + JsStrVariant::Utf16(v) => JsStr::utf16(v.get_unchecked(index)), + } + } + } } impl<'a> JsSliceIndex<'a> for std::ops::RangeFull { @@ -364,4 +451,12 @@ impl<'a> JsSliceIndex<'a> for std::ops::RangeFull { fn get(value: JsStr<'a>, _index: Self) -> Option { Some(value) } + + /// # Safety + /// + /// Caller must ensure the index is not out of bounds + #[inline] + unsafe fn get_unchecked(value: JsStr<'a>, _index: Self) -> Self::Value { + value + } }