dangerous/input/span.rs
1use core::ops::Range;
2use core::ptr::NonNull;
3use core::slice;
4
5use crate::display::InputDisplay;
6use crate::fmt;
7use crate::input::{Input, MaybeString};
8
9/// Range of [`Input`].
10///
11/// Spans are specific to the input chain they were created in as the range is
12/// stored as raw start and end pointers.
13///
14/// You can create a span from either [`Input::span()`] or from a raw slice via
15/// [`Span::from()`].
16///
17/// [`Input`]: crate::Input
18/// [`Input::span()`]: crate::Input::span()
19#[must_use]
20#[derive(Copy, Clone, Eq, PartialEq, Hash)]
21pub struct Span {
22 start: NonNull<u8>,
23 end: NonNull<u8>,
24}
25
26impl Span {
27 /// Returns the number of bytes spanned.
28 #[must_use]
29 #[inline(always)]
30 pub fn len(self) -> usize {
31 self.end.as_ptr() as usize - self.start.as_ptr() as usize
32 }
33
34 /// Returns `true` if no bytes are spanned.
35 #[must_use]
36 #[inline(always)]
37 pub fn is_empty(self) -> bool {
38 self.start == self.end
39 }
40
41 /// Returns `true` if the span is completely within the bounds of the
42 /// specified parent.
43 ///
44 /// # Examples
45 ///
46 /// ```
47 /// use dangerous::Span;
48 ///
49 /// let bytes = [0_u8; 64];
50 ///
51 /// // Within
52 /// let parent = Span::from(&bytes[16..32]);
53 /// let child = Span::from(&bytes[20..24]);
54 /// assert!(child.is_within(parent));
55 /// assert!(parent.is_within(parent));
56 ///
57 /// // Left out of bound
58 /// let parent = Span::from(&bytes[16..32]);
59 /// let child = Span::from(&bytes[15..24]);
60 /// assert!(!child.is_within(parent));
61 ///
62 /// // Right out of bound
63 /// let parent = Span::from(&bytes[16..32]);
64 /// let child = Span::from(&bytes[20..33]);
65 /// assert!(!child.is_within(parent));
66 ///
67 /// // Both out of bound
68 /// let parent = Span::from(&bytes[16..32]);
69 /// let child = Span::from(&bytes[15..33]);
70 /// assert!(!child.is_within(parent));
71 /// ```
72 #[must_use]
73 #[inline(always)]
74 pub fn is_within(self, other: Span) -> bool {
75 other.start <= self.start && other.end >= self.end
76 }
77
78 /// Returns `true` if `self` points to the start of `other`, spanning no bytes.
79 ///
80 /// # Example
81 ///
82 /// ```
83 /// use dangerous::Span;
84 ///
85 /// let bytes = &[1, 2, 3, 4][..];
86 /// let span = Span::from(bytes);
87 ///
88 /// assert!(span.start().is_start_of(span));
89 /// assert!(!span.is_start_of(span));
90 /// ```
91 #[must_use]
92 #[inline(always)]
93 pub fn is_start_of(self, other: Span) -> bool {
94 self.is_empty() && other.start == self.start
95 }
96
97 /// Returns `true` if `self` points to the end of `other`, spanning no bytes.
98 ///
99 /// # Example
100 ///
101 /// ```
102 /// use dangerous::Span;
103 ///
104 /// let bytes = &[1, 2, 3, 4][..];
105 /// let span = Span::from(bytes);
106 ///
107 /// assert!(span.end().is_end_of(span));
108 /// assert!(!span.is_end_of(span));
109 /// ```
110 #[must_use]
111 #[inline(always)]
112 pub fn is_end_of(self, other: Span) -> bool {
113 self.is_empty() && other.end == self.end
114 }
115
116 /// Returns `true` if `self` overlaps the start of `other`.
117 ///
118 /// # Example
119 ///
120 /// ```
121 /// use dangerous::Span;
122 ///
123 /// let all = b"0123456789";
124 /// let a = Span::from(&all[0..9]);
125 /// let b = Span::from(&all[6..9]);
126 ///
127 /// assert!(a.is_overlapping_start_of(b));
128 ///
129 /// ```
130 #[must_use]
131 #[inline(always)]
132 pub fn is_overlapping_start_of(self, other: Span) -> bool {
133 other.start > self.start
134 }
135
136 /// Returns `true` if `self` overlaps the end of `other`.
137 #[must_use]
138 #[inline(always)]
139 pub fn is_overlapping_end_of(self, other: Span) -> bool {
140 other.end < self.end
141 }
142
143 /// Returns `true` if `self`'s start is within `other`.
144 #[must_use]
145 #[inline(always)]
146 #[allow(clippy::suspicious_operation_groupings)]
147 pub fn is_start_within(self, other: Span) -> bool {
148 self.start >= other.start && self.start < other.end
149 }
150
151 /// Returns `true` if `self`'s end is within `other`.
152 #[must_use]
153 #[inline(always)]
154 #[allow(clippy::suspicious_operation_groupings)]
155 pub fn is_end_within(self, other: Span) -> bool {
156 self.end >= other.start && self.end < other.end
157 }
158
159 /// Returns a span pointing to the start of self, spanning no bytes.
160 pub fn start(self) -> Self {
161 Self {
162 start: self.start,
163 end: self.start,
164 }
165 }
166
167 /// Returns a span pointing to the end of self, spanning no bytes.
168 pub fn end(self) -> Self {
169 Self {
170 start: self.end,
171 end: self.end,
172 }
173 }
174
175 /// Returns the sub slice of the provided parent `self` refers to or `None`
176 /// if `self` is not within the parent or does not align with start and end
177 /// token boundaries.
178 ///
179 /// You can get a span of [`Input`], `&str` or `&[u8]`.
180 ///
181 /// # Example
182 ///
183 /// ```
184 /// use dangerous::Span;
185 ///
186 /// let parent = &[1, 2, 3, 4][..];
187 /// let sub = &parent[1..2];
188 /// assert_eq!(Span::from(sub).of(parent), Some(sub));
189 ///
190 /// let non_span = Span::from(&[1, 2, 2, 4][..]);
191 /// assert_eq!(non_span.of(parent), None);
192 /// ```
193 #[must_use]
194 pub fn of<P>(self, parent: P) -> Option<P>
195 where
196 P: Parent,
197 {
198 parent.extract(self)
199 }
200
201 /// Returns `Some(Range)` with the `start` and `end` offsets of `self`
202 /// within the `parent`. `None` is returned if `self` is not within in the
203 /// `parent`.
204 ///
205 /// # Example
206 ///
207 /// ```
208 /// use dangerous::Span;
209 ///
210 /// let parent = &[1, 2, 3, 4][..];
211 /// let sub_range = 1..2;
212 /// let sub = &parent[sub_range.clone()];
213 ///
214 /// assert_eq!(Span::from(sub).range_of(parent.into()), Some(sub_range))
215 /// ```
216 #[must_use]
217 #[inline(always)]
218 pub fn range_of(self, parent: Span) -> Option<Range<usize>> {
219 if self.is_within(parent) {
220 let start_offset = self.start.as_ptr() as usize - parent.start.as_ptr() as usize;
221 let end_offset = self.end.as_ptr() as usize - parent.start.as_ptr() as usize;
222 Some(start_offset..end_offset)
223 } else {
224 None
225 }
226 }
227
228 /// Returns `None` if the span is empty, `Some(Self)` if not.
229 ///
230 /// # Example
231 ///
232 /// ```
233 /// use dangerous::Span;
234 ///
235 /// let bytes = &[0][..];
236 /// assert!(Span::from(bytes).non_empty().is_some());
237 ///
238 /// let bytes = &[][..];
239 /// assert!(Span::from(bytes).non_empty().is_none());
240 /// ```
241 #[must_use]
242 #[inline(always)]
243 pub fn non_empty(self) -> Option<Self> {
244 if self.is_empty() {
245 None
246 } else {
247 Some(self)
248 }
249 }
250
251 /// Wraps the span with improved debugging support given the containing
252 /// input.
253 #[inline(always)]
254 #[allow(clippy::needless_pass_by_value)]
255 pub fn debug_for(self, input: MaybeString<'_>) -> DebugFor<'_> {
256 DebugFor {
257 bytes: input.as_dangerous_bytes(),
258 str_hint: input.is_string(),
259 span: self,
260 }
261 }
262}
263
264impl fmt::DisplayBase for Span {
265 fn fmt(&self, w: &mut dyn fmt::Write) -> fmt::Result {
266 w.write_str("(ptr: ")?;
267 w.write_usize(self.start.as_ptr() as usize)?;
268 w.write_str(", len: ")?;
269 w.write_usize(self.len())?;
270 w.write_char(')')
271 }
272}
273
274impl From<&[u8]> for Span {
275 #[inline(always)]
276 fn from(value: &[u8]) -> Self {
277 let range = value.as_ptr_range();
278 // SAFETY: it is invalid for a slice ptr to be null.
279 // See: https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html
280 unsafe {
281 Self {
282 start: NonNull::new_unchecked(range.start as _),
283 end: NonNull::new_unchecked(range.end as _),
284 }
285 }
286 }
287}
288
289impl From<&str> for Span {
290 #[inline(always)]
291 fn from(value: &str) -> Self {
292 Self::from(value.as_bytes())
293 }
294}
295
296impl fmt::Debug for Span {
297 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
298 f.debug_struct("Span")
299 .field("ptr", &self.start)
300 .field("len", &self.len())
301 .finish()
302 }
303}
304
305// SAFETY: Span can only dereference the data pointed to if the data is present
306// and passed as a parent. This makes the internal pointers safe to alias across
307// threads as the parent data enforces the aliasing rules.
308unsafe impl Send for Span {}
309
310// SAFETY: Span can only dereference the data pointed to if the data is present
311// and passed as a parent. This makes the internal pointers safe to alias across
312// threads as the parent data enforces the aliasing rules.
313unsafe impl Sync for Span {}
314
315#[must_use]
316pub struct DebugFor<'a> {
317 span: Span,
318 str_hint: bool,
319 bytes: &'a [u8],
320}
321
322impl<'a> fmt::Debug for DebugFor<'a> {
323 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
324 match self.span.of(self.bytes) {
325 Some(valid) => {
326 let display = InputDisplay::from_bytes(valid).with_formatter(f);
327 let display = if self.str_hint {
328 display.str_hint()
329 } else {
330 display
331 };
332 f.debug_tuple("Span").field(&display).finish()
333 }
334 None => fmt::Debug::fmt(&self.span, f),
335 }
336 }
337}
338
339pub trait Parent: Sized {
340 fn extract(self, span: Span) -> Option<Self>;
341}
342
343impl Parent for &[u8] {
344 #[inline(always)]
345 fn extract(self, span: Span) -> Option<Self> {
346 if span.is_within(self.into()) {
347 // SAFETY: we have checked that the slice is valid within the
348 // parent, so we can create a slice from our bounds.
349 unsafe { Some(slice::from_raw_parts(span.start.as_ptr(), span.len())) }
350 } else {
351 None
352 }
353 }
354}
355
356impl Parent for &str {
357 #[inline]
358 fn extract(self, span: Span) -> Option<Self> {
359 span.range_of(self.into()).and_then(|range| {
360 if self.is_char_boundary(range.start) && self.is_char_boundary(range.end) {
361 Some(&self[range])
362 } else {
363 None
364 }
365 })
366 }
367}
368
369impl<'i, T> Parent for T
370where
371 T: Input<'i>,
372{
373 #[inline]
374 fn extract(self, span: Span) -> Option<Self> {
375 span.range_of(self.span()).and_then(|range| {
376 if self.verify_token_boundary(range.start).is_ok()
377 && self.verify_token_boundary(range.end).is_ok()
378 {
379 // SAFETY: we have checked that the range start and end are
380 // valid boundary indexes within the parent, so we can split
381 // with them.
382 let sub = unsafe {
383 let (_, tail) = self.split_at_byte_unchecked(range.start);
384 let (sub, _) = tail.split_at_byte_unchecked(range.end - range.start);
385 sub
386 };
387 Some(sub)
388 } else {
389 None
390 }
391 })
392 }
393}