Skip to main content

parse_net/
range.rs

1//! `Range`, `Content-Range`, and `If-Range` (RFC 9110 section 14), with
2//! resolution against a representation length and budgets against abusive
3//! range sets.
4//!
5//! # Examples
6//!
7//! ```
8//! use parse_net::{self as pn, ByteRange, RangeOutcome, range};
9//!
10//! let mut out = [ByteRange { start: 0, end: 0 }; 8];
11//! assert_eq!(pn::byte_ranges(b"bytes=0-99, -100", 1000, &mut out)?, RangeOutcome::Ranges(2));
12//! assert_eq!(out[1], ByteRange { start: 900, end: 999 });
13//! assert_eq!(pn::byte_ranges(b"bytes=2000-", 1000, &mut out)?, RangeOutcome::Unsatisfiable);
14//! assert_eq!(pn::byte_ranges(b"items=0-5", 1000, &mut out)?, RangeOutcome::NotBytes);
15//!
16//! let mut buf = [0u8; 32];
17//! let mut sink = pn::SliceSink::new(&mut buf);
18//! range::write_content_range(out[0], Some(1000), &mut sink).expect("room");
19//! assert_eq!(sink.written(), b"bytes 0-99/1000");
20//! # Ok::<(), pn::ParseError>(())
21//! ```
22
23use crate::{
24    class,
25    date::HttpDate,
26    error::{ErrorKind, Result, WriteError, err},
27    etag::EntityTag,
28    limits::Limits,
29    num::parse_u64,
30    text::{skip_ows, trim_ows},
31    write::{CountSink, Sink, reserve, write_decimal_u64},
32};
33
34/// One `range-spec` of a `bytes` range set.
35///
36/// # Examples
37///
38/// ```
39/// use parse_net::{range, Limits, RangeSpec};
40///
41/// let h = range::parse(b"bytes=0-9, 50-, -5", &Limits::DEFAULT)?;
42/// let specs: Vec<_> = h.specs().collect();
43/// assert_eq!(specs, [RangeSpec::FromTo(0, 9), RangeSpec::From(50), RangeSpec::Suffix(5)]);
44/// # Ok::<(), parse_net::ParseError>(())
45/// ```
46#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
47pub enum RangeSpec {
48    /// `first-` : from `first` to the end.
49    From(u64),
50    /// `first-last`, both inclusive.
51    FromTo(u64, u64),
52    /// `-length`: the last `length` bytes.
53    Suffix(u64),
54}
55
56/// A resolved byte range, both ends inclusive.
57///
58/// # Examples
59///
60/// ```
61/// use parse_net::{byte_ranges, ByteRange, RangeOutcome};
62///
63/// let mut out = [ByteRange { start: 0, end: 0 }; 1];
64/// assert_eq!(byte_ranges(b"bytes=-10", 100, &mut out)?, RangeOutcome::Ranges(1));
65/// assert_eq!((out[0], out[0].len()), (ByteRange { start: 90, end: 99 }, 10));
66/// # Ok::<(), parse_net::ParseError>(())
67/// ```
68#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
69pub struct ByteRange {
70    /// The first byte.
71    pub start: u64,
72    /// The last byte (inclusive).
73    pub end: u64,
74}
75
76impl ByteRange {
77    /// The number of bytes, `end - start + 1`, saturating at `u64::MAX`; zero
78    /// when `end < start`.
79    ///
80    /// # Examples
81    ///
82    /// ```
83    /// assert_eq!(parse_net::ByteRange { start: 0, end: 499 }.len(), 500);
84    /// ```
85    #[must_use]
86    pub const fn len(&self) -> u64 {
87        if self.end < self.start {
88            0
89        } else {
90            (self.end - self.start).saturating_add(1)
91        }
92    }
93
94    /// Whether the range holds no bytes (only possible when `end < start`).
95    ///
96    /// # Examples
97    ///
98    /// ```
99    /// use parse_net::ByteRange;
100    ///
101    /// assert!(ByteRange { start: 5, end: 4 }.is_empty());
102    /// assert!(!ByteRange { start: 5, end: 5 }.is_empty());
103    /// ```
104    #[must_use]
105    pub const fn is_empty(&self) -> bool {
106        self.end < self.start
107    }
108}
109
110/// What to do with a `Range` request.
111///
112/// # Examples
113///
114/// ```
115/// use parse_net::{byte_ranges, ByteRange, RangeOutcome};
116///
117/// let mut out = [ByteRange { start: 0, end: 0 }; 4];
118/// assert_eq!(byte_ranges(b"bytes=500-", 1000, &mut out)?, RangeOutcome::Ranges(1));
119/// assert_eq!(byte_ranges(b"bytes=1000-", 1000, &mut out)?, RangeOutcome::Unsatisfiable);
120/// # Ok::<(), parse_net::ParseError>(())
121/// ```
122#[non_exhaustive]
123#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
124pub enum RangeOutcome {
125    /// Serve these ranges (206): the first `n` entries of the output.
126    Ranges(usize),
127    /// No range is satisfiable: answer 416 with `Content-Range: bytes */length`.
128    Unsatisfiable,
129    /// Ignore the field and serve the whole representation (200): the set is
130    /// abusive (too many ranges, too many overlapping, too much out of order)
131    /// or does not fit the output.
132    Ignore,
133    /// Another range unit: ignore the field and serve the whole
134    /// representation.
135    NotBytes,
136}
137
138/// A validated `Range` value.
139///
140/// # Examples
141///
142/// ```
143/// use parse_net::{range, Limits};
144///
145/// let h = range::parse(b"bytes=0-99, 200-299", &Limits::DEFAULT)?;
146/// assert!(h.is_bytes());
147/// assert_eq!(h.specs().count(), 2);
148/// # Ok::<(), parse_net::ParseError>(())
149/// ```
150#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
151pub struct RangeHeader<'a> {
152    unit: &'a [u8],
153    /// The validated range set.
154    set: &'a [u8],
155    count: usize,
156}
157
158impl<'a> RangeHeader<'a> {
159    /// Whether the unit is `bytes` (case-insensitive).
160    ///
161    /// # Examples
162    ///
163    /// ```
164    /// use parse_net::{range, Limits};
165    ///
166    /// assert!(range::parse(b"BYTES=0-1", &Limits::DEFAULT)?.is_bytes());
167    /// assert!(!range::parse(b"items=0-1", &Limits::DEFAULT)?.is_bytes());
168    /// # Ok::<(), parse_net::ParseError>(())
169    /// ```
170    #[must_use]
171    pub fn is_bytes(&self) -> bool {
172        self.unit.eq_ignore_ascii_case(b"bytes")
173    }
174
175    /// The range unit, as sent.
176    ///
177    /// # Examples
178    ///
179    /// ```
180    /// use parse_net::{range, Limits};
181    ///
182    /// assert_eq!(range::parse(b"items=0-5", &Limits::DEFAULT)?.unit(), b"items");
183    /// # Ok::<(), parse_net::ParseError>(())
184    /// ```
185    #[must_use]
186    pub const fn unit(&self) -> &'a [u8] {
187        self.unit
188    }
189
190    /// The range specs, in request order; empty unless the unit is `bytes`.
191    ///
192    /// # Examples
193    ///
194    /// ```
195    /// use parse_net::{range, Limits, RangeSpec};
196    ///
197    /// assert_eq!(range::parse(b"bytes=-1", &Limits::DEFAULT)?.specs().next(), Some(RangeSpec::Suffix(1)));
198    /// assert_eq!(range::parse(b"items=0-5", &Limits::DEFAULT)?.specs().count(), 0);
199    /// # Ok::<(), parse_net::ParseError>(())
200    /// ```
201    pub fn specs(&self) -> impl Iterator<Item = RangeSpec> + use<'a> {
202        let set = if self.is_bytes() { self.set } else { &[] };
203        Elements { s: set }.filter_map(|e| spec(e).ok())
204    }
205
206    /// The number of members in the range set, whatever the unit (so it can
207    /// be nonzero when [`specs`](Self::specs) is empty).
208    ///
209    /// # Examples
210    ///
211    /// ```
212    /// use parse_net::{range, Limits};
213    ///
214    /// let h = range::parse(b"items=0-5, 9-10", &Limits::DEFAULT)?;
215    /// assert_eq!((h.len(), h.specs().count()), (2, 0));
216    /// # Ok::<(), parse_net::ParseError>(())
217    /// ```
218    #[must_use]
219    pub const fn len(&self) -> usize {
220        self.count
221    }
222
223    /// Whether the set is empty (never true for a parsed value).
224    ///
225    /// # Examples
226    ///
227    /// ```
228    /// use parse_net::{range, Limits};
229    ///
230    /// assert!(!range::parse(b"bytes=0-0", &Limits::DEFAULT)?.is_empty());
231    /// # Ok::<(), parse_net::ParseError>(())
232    /// ```
233    #[must_use]
234    pub const fn is_empty(&self) -> bool {
235        self.count == 0
236    }
237}
238
239/// Parses one `int-range / suffix-range`.
240/// The members of a range set: the text between commas, OWS trimmed, empty
241/// members skipped. The set is validated before it is walked.
242struct Elements<'a> {
243    s: &'a [u8],
244}
245
246impl<'a> Iterator for Elements<'a> {
247    type Item = &'a [u8];
248
249    fn next(&mut self) -> Option<&'a [u8]> {
250        loop {
251            if self.s.is_empty() {
252                return None;
253            }
254            let end = crate::scan::find_byte(b',', self.s).unwrap_or(self.s.len());
255            let e = trim_ows(self.s.get(..end).unwrap_or(&[]));
256            self.s = self.s.get(end + 1..).unwrap_or(&[]);
257            if !e.is_empty() {
258                return Some(e);
259            }
260        }
261    }
262}
263
264fn spec(e: &[u8]) -> Result<RangeSpec> {
265    let Some(dash) = e.iter().position(|&b| b == b'-') else {
266        return err(ErrorKind::InvalidRange, e.len());
267    };
268    let (first, last) = (
269        e.get(..dash).unwrap_or(&[]),
270        e.get(dash + 1..).unwrap_or(&[]),
271    );
272    match (first.is_empty(), last.is_empty()) {
273        (true, true) => err(ErrorKind::InvalidRange, 0),
274        (true, false) => Ok(RangeSpec::Suffix(
275            parse_u64(last).map_err(|x| x.shifted(dash + 1))?,
276        )),
277        (false, true) => Ok(RangeSpec::From(parse_u64(first)?)),
278        (false, false) => {
279            let f = parse_u64(first)?;
280            let l = parse_u64(last).map_err(|x| x.shifted(dash + 1))?;
281            if f > l {
282                return err(ErrorKind::InvalidRange, 0);
283            }
284            Ok(RangeSpec::FromTo(f, l))
285        }
286    }
287}
288
289/// Parses `range-unit "=" range-set` (RFC 9110 section 14.1.2).
290///
291/// The unit compares case-insensitively. A `bytes` set must hold only
292/// `first-last`, `first-`, and `-length` specs; another unit's set is
293/// checked as `other-range` bytes and not interpreted.
294///
295/// # Errors
296///
297/// [`ErrorKind::NotToken`] for the unit; [`ErrorKind::InvalidRange`] for a
298/// missing `=`, whitespace around it, an empty set, `first > last`, or a
299/// malformed spec; [`ErrorKind::InvalidNumber`], [`ErrorKind::NumberOverflow`];
300/// the list errors.
301///
302/// # Examples
303///
304/// ```
305/// use parse_net::{range, Limits, RangeSpec};
306///
307/// let h = range::parse(b"bytes=0-0, -5, 10-", &Limits::DEFAULT)?;
308/// assert_eq!(h.specs().collect::<Vec<_>>(), [RangeSpec::FromTo(0, 0), RangeSpec::Suffix(5), RangeSpec::From(10)]);
309/// assert!(range::parse(b"bytes=5-4", &Limits::DEFAULT).is_err());
310/// # Ok::<(), parse_net::ParseError>(())
311/// ```
312pub fn parse<'a>(value: &'a [u8], limits: &Limits) -> Result<RangeHeader<'a>> {
313    let lead = skip_ows(value);
314    let v = trim_ows(value);
315    let unit_len = v.iter().take_while(|&&b| class::is_tchar(b)).count();
316    if unit_len == 0 {
317        return err(ErrorKind::NotToken, lead);
318    }
319    if v.get(unit_len) != Some(&b'=') {
320        return err(ErrorKind::InvalidRange, lead + unit_len);
321    }
322    let unit = v.get(..unit_len).unwrap_or(&[]);
323    let set_at = unit_len + 1;
324    let set_text = v.get(set_at..).unwrap_or(&[]);
325    if set_text.first().is_some_and(|&b| crate::text::is_ows(b)) {
326        return err(ErrorKind::InvalidRange, lead + set_at);
327    }
328    // One pass over the set: each member is found by its comma, trimmed,
329    // and checked; empty members are skipped up to the limit.
330    let bytes = unit.eq_ignore_ascii_case(b"bytes");
331    let (mut i, mut count, mut empty) = (0usize, 0usize, 0usize);
332    while i < set_text.len() {
333        let rest = set_text.get(i..).unwrap_or(&[]);
334        let end = crate::scan::find_byte(b',', rest).unwrap_or(rest.len());
335        let raw = rest.get(..end).unwrap_or(&[]);
336        let e = trim_ows(raw);
337        let at = lead + set_at + i + skip_ows(raw);
338        if e.is_empty() {
339            empty += 1;
340            if empty > usize::from(limits.empty_elements) {
341                return err(ErrorKind::TooManyEmptyElements, at);
342            }
343        } else {
344            count += 1;
345            if count > usize::from(limits.list_members) {
346                return err(ErrorKind::TooManyMembers, at);
347            }
348            if bytes {
349                spec(e).map(|_| ()).map_err(|x| x.shifted(at))?;
350            } else if let Some(j) = e.iter().position(|&b| !class::is_vchar(b)) {
351                return Err(match e.get(j) {
352                    Some(&b) if crate::scan::is_control(b) => {
353                        crate::ParseError::new(crate::scan::control_kind(b), at + j)
354                    }
355                    _ => crate::ParseError::new(ErrorKind::InvalidRange, at + j),
356                });
357            }
358        }
359        i += end + 1;
360    }
361    if count == 0 {
362        return err(ErrorKind::InvalidRange, lead + set_at);
363    }
364    Ok(RangeHeader {
365        unit,
366        set: set_text,
367        count,
368    })
369}
370
371/// Resolves a parsed `Range` against a representation of `length` bytes.
372///
373/// Suffix ranges become absolute; a last position beyond the end is clamped;
374/// a range starting at or after the end, and a zero-length suffix, are
375/// dropped. More than [`Limits::max_ranges`] ranges, more than two ranges
376/// that overlap another, or more than two ranges that start before the one
377/// before them give [`RangeOutcome::Ignore`] (RFC 9110 section 14.2 allows
378/// ignoring abusive sets). With `coalesce`, overlapping and adjacent ranges
379/// are merged into ascending order (this changes the shape of a multipart
380/// response, so it is the caller's choice). An `out` too small for the
381/// ranges gives `Ignore`: the set is never silently truncated.
382///
383/// # Examples
384///
385/// ```
386/// use parse_net::{range, ByteRange, Limits, RangeOutcome};
387///
388/// let h = range::parse(b"bytes=0-10, 5-20, 40-49", &Limits::DEFAULT)?;
389/// let mut out = [ByteRange { start: 0, end: 0 }; 4];
390/// assert_eq!(range::resolve(&h, 100, true, &Limits::DEFAULT, &mut out), RangeOutcome::Ranges(2));
391/// assert_eq!(out[..2], [ByteRange { start: 0, end: 20 }, ByteRange { start: 40, end: 49 }]);
392/// # Ok::<(), parse_net::ParseError>(())
393/// ```
394#[must_use]
395pub fn resolve(
396    h: &RangeHeader<'_>,
397    length: u64,
398    coalesce: bool,
399    limits: &Limits,
400    out: &mut [ByteRange],
401) -> RangeOutcome {
402    if !h.is_bytes() {
403        return RangeOutcome::NotBytes;
404    }
405    if h.len() > usize::from(limits.ranges) {
406        return RangeOutcome::Ignore;
407    }
408    let mut n = 0usize;
409    let mut descents = 0usize;
410    let mut previous_start: Option<u64> = None;
411    for s in h.specs() {
412        let Some(r) = absolute(s, length) else {
413            continue;
414        };
415        if previous_start.is_some_and(|p| r.start < p) {
416            descents += 1;
417        }
418        previous_start = Some(r.start);
419        match out.get_mut(n) {
420            Some(slot) => *slot = r,
421            None => return RangeOutcome::Ignore,
422        }
423        n += 1;
424    }
425    if n == 0 {
426        return RangeOutcome::Unsatisfiable;
427    }
428    let resolved = out.get_mut(..n).unwrap_or(&mut []);
429    let overlapping = resolved
430        .iter()
431        .enumerate()
432        .filter(|&(i, a)| {
433            resolved
434                .iter()
435                .enumerate()
436                .any(|(j, b)| i != j && a.start <= b.end && b.start <= a.end)
437        })
438        .count();
439    if overlapping > 2 || descents > 2 {
440        return RangeOutcome::Ignore;
441    }
442    if coalesce {
443        // Insertion sort: at most `max_ranges` entries.
444        for i in 1..resolved.len() {
445            let mut j = i;
446            while j > 0 {
447                let (Some(&a), Some(&b)) = (resolved.get(j - 1), resolved.get(j)) else {
448                    break;
449                };
450                if a.start <= b.start {
451                    break;
452                }
453                resolved.swap(j - 1, j);
454                j -= 1;
455            }
456        }
457        let mut merged = 0usize;
458        for i in 0..resolved.len() {
459            let Some(&r) = resolved.get(i) else { break };
460            match merged.checked_sub(1).and_then(|m| resolved.get_mut(m)) {
461                Some(last) if r.start <= last.end.saturating_add(1) => {
462                    last.end = last.end.max(r.end);
463                }
464                _ => {
465                    if let Some(slot) = resolved.get_mut(merged) {
466                        *slot = r;
467                    }
468                    merged += 1;
469                }
470            }
471        }
472        n = merged;
473    }
474    RangeOutcome::Ranges(n)
475}
476
477/// The absolute range of `s` in a representation of `length` bytes, or
478/// `None` when it is unsatisfiable.
479fn absolute(s: RangeSpec, length: u64) -> Option<ByteRange> {
480    let last = length.checked_sub(1)?;
481    match s {
482        RangeSpec::From(f) if f <= last => Some(ByteRange {
483            start: f,
484            end: last,
485        }),
486        RangeSpec::FromTo(f, l) if f <= last => Some(ByteRange {
487            start: f,
488            end: l.min(last),
489        }),
490        RangeSpec::Suffix(n) if n > 0 => Some(ByteRange {
491            start: length - n.min(length),
492            end: last,
493        }),
494        _ => None,
495    }
496}
497
498/// Parses and resolves `Range` against `length` with the default limits and
499/// no coalescing.
500///
501/// # Errors
502///
503/// As [`parse`]. RFC 9110 lets a server ignore a `Range` it cannot read:
504/// treat an error as [`RangeOutcome::Ignore`] unless the request should be
505/// refused.
506///
507/// # Examples
508///
509/// ```
510/// use parse_net::{byte_ranges, ByteRange, RangeOutcome};
511///
512/// let mut out = [ByteRange { start: 0, end: 0 }; 1];
513/// assert_eq!(byte_ranges(b"bytes=0-0, 2-2", 10, &mut out)?, RangeOutcome::Ignore); // `out` too small
514/// # Ok::<(), parse_net::ParseError>(())
515/// ```
516pub fn byte_ranges(value: &[u8], length: u64, out: &mut [ByteRange]) -> Result<RangeOutcome> {
517    let h = parse(value, &Limits::DEFAULT)?;
518    Ok(resolve(&h, length, false, &Limits::DEFAULT, out))
519}
520
521/// A parsed `Content-Range` value.
522///
523/// # Examples
524///
525/// ```
526/// use parse_net::{range::parse_content_range, ContentRange};
527///
528/// let ContentRange::Satisfied { range, complete } = parse_content_range(b"bytes 42-1233/*")? else { unreachable!() };
529/// assert_eq!((range.len(), complete), (1192, None));
530/// # Ok::<(), parse_net::ParseError>(())
531/// ```
532#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
533pub enum ContentRange {
534    /// `bytes first-last/complete` or `bytes first-last/*`.
535    Satisfied {
536        /// The range sent.
537        range: ByteRange,
538        /// The complete length, when known.
539        complete: Option<u64>,
540    },
541    /// `bytes */complete`, sent with 416.
542    Unsatisfied {
543        /// The complete length.
544        complete: u64,
545    },
546}
547
548/// Parses `Content-Range` (RFC 9110 section 14.4) for the `bytes` unit.
549///
550/// # Errors
551///
552/// [`ErrorKind::InvalidRange`] (another unit, a missing space, `last < first`,
553/// `last >= complete`, a malformed shape); [`ErrorKind::InvalidNumber`],
554/// [`ErrorKind::NumberOverflow`].
555///
556/// # Examples
557///
558/// ```
559/// use parse_net::{range::parse_content_range, ByteRange, ContentRange};
560///
561/// assert_eq!(parse_content_range(b"bytes 0-499/1234")?,
562///            ContentRange::Satisfied { range: ByteRange { start: 0, end: 499 }, complete: Some(1234) });
563/// assert_eq!(parse_content_range(b"bytes */0")?, ContentRange::Unsatisfied { complete: 0 });
564/// assert!(parse_content_range(b"bytes 0-5/5").is_err());
565/// # Ok::<(), parse_net::ParseError>(())
566/// ```
567pub fn parse_content_range(value: &[u8]) -> Result<ContentRange> {
568    let lead = skip_ows(value);
569    let v = trim_ows(value);
570    inner_content_range(v).map_err(|e| e.shifted(lead))
571}
572
573fn inner_content_range(v: &[u8]) -> Result<ContentRange> {
574    let unit = v.get(..5).unwrap_or(&[]);
575    if !unit.eq_ignore_ascii_case(b"bytes") || v.get(5) != Some(&b' ') {
576        return err(ErrorKind::InvalidRange, 0);
577    }
578    let rest = v.get(6..).unwrap_or(&[]);
579    let Some(slash) = rest.iter().position(|&b| b == b'/') else {
580        return err(ErrorKind::InvalidRange, v.len());
581    };
582    let (range_text, complete_text) = (
583        rest.get(..slash).unwrap_or(&[]),
584        rest.get(slash + 1..).unwrap_or(&[]),
585    );
586    let complete_at = 6 + slash + 1;
587    if range_text == b"*" {
588        let complete = parse_u64(complete_text).map_err(|e| e.shifted(complete_at))?;
589        return Ok(ContentRange::Unsatisfied { complete });
590    }
591    let complete = if complete_text == b"*" {
592        None
593    } else {
594        Some(parse_u64(complete_text).map_err(|e| e.shifted(complete_at))?)
595    };
596    let Some(dash) = range_text.iter().position(|&b| b == b'-') else {
597        return err(ErrorKind::InvalidRange, 6);
598    };
599    let first = parse_u64(range_text.get(..dash).unwrap_or(&[])).map_err(|e| e.shifted(6))?;
600    let last = parse_u64(range_text.get(dash + 1..).unwrap_or(&[]))
601        .map_err(|e| e.shifted(6 + dash + 1))?;
602    if last < first || complete.is_some_and(|c| last >= c) {
603        return err(ErrorKind::InvalidRange, 6);
604    }
605    Ok(ContentRange::Satisfied {
606        range: ByteRange {
607            start: first,
608            end: last,
609        },
610        complete,
611    })
612}
613
614fn put_content_range(
615    r: ByteRange,
616    complete: Option<u64>,
617    out: &mut impl Sink,
618) -> core::result::Result<(), WriteError> {
619    out.put(b"bytes ")?;
620    write_decimal_u64(r.start, out)?;
621    out.put(b"-")?;
622    write_decimal_u64(r.end, out)?;
623    out.put(b"/")?;
624    match complete {
625        Some(c) => write_decimal_u64(c, out)?,
626        None => out.put(b"*")?,
627    }
628    Ok(())
629}
630
631/// Writes `bytes first-last/complete`, or `bytes first-last/*` when the
632/// complete length is unknown.
633///
634/// # Errors
635///
636/// [`WriteError::OutOfRange`] when `end < start` or `end >= complete`;
637/// [`WriteError::Full`] (nothing written when the sink reports its room).
638///
639/// # Examples
640///
641/// ```
642/// use parse_net::{range::write_content_range, ByteRange, SliceSink, WriteError};
643///
644/// let mut buf = [0u8; 32];
645/// let mut out = SliceSink::new(&mut buf);
646/// write_content_range(ByteRange { start: 0, end: 9 }, None, &mut out)?;
647/// assert_eq!(out.written(), b"bytes 0-9/*");
648/// assert_eq!(write_content_range(ByteRange { start: 0, end: 9 }, Some(5), &mut out), Err(WriteError::OutOfRange));
649/// # Ok::<(), parse_net::WriteError>(())
650/// ```
651pub fn write_content_range(
652    r: ByteRange,
653    complete: Option<u64>,
654    out: &mut impl Sink,
655) -> core::result::Result<(), WriteError> {
656    if r.end < r.start || complete.is_some_and(|c| r.end >= c) {
657        return Err(WriteError::OutOfRange);
658    }
659    let mut count = CountSink::new();
660    put_content_range(r, complete, &mut count)?;
661    reserve(out, count.count())?;
662    put_content_range(r, complete, out)
663}
664
665/// Writes `bytes */complete`, for a 416 response.
666///
667/// # Errors
668///
669/// [`WriteError::Full`] (nothing written when the sink reports its room).
670///
671/// # Examples
672///
673/// ```
674/// use parse_net::{range::write_unsatisfied, SliceSink};
675///
676/// let mut buf = [0u8; 32];
677/// let mut out = SliceSink::new(&mut buf);
678/// write_unsatisfied(1234, &mut out).expect("room");
679/// assert_eq!(out.written(), b"bytes */1234");
680/// ```
681pub fn write_unsatisfied(
682    complete: u64,
683    out: &mut impl Sink,
684) -> core::result::Result<(), WriteError> {
685    let mut count = CountSink::new();
686    count.put(b"bytes */")?;
687    write_decimal_u64(complete, &mut count)?;
688    reserve(out, count.count())?;
689    out.put(b"bytes */")?;
690    write_decimal_u64(complete, out)
691}
692
693/// A parsed `If-Range` value.
694///
695/// # Examples
696///
697/// ```
698/// use parse_net::{range::parse_if_range, HttpDate, IfRange};
699///
700/// let date = b"Wed, 21 Oct 2026 07:28:00 GMT";
701/// let cond = parse_if_range(date)?;
702/// assert_eq!(cond, IfRange::Date(HttpDate::parse_imf_fixdate(date)?));
703/// assert!(cond.matches(None, Some(HttpDate::parse_imf_fixdate(date)?)));
704/// # Ok::<(), parse_net::ParseError>(())
705/// ```
706#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
707pub enum IfRange<'a> {
708    /// An HTTP-date.
709    Date(HttpDate),
710    /// An entity tag.
711    Tag(EntityTag<'a>),
712}
713
714impl IfRange<'_> {
715    /// Whether the condition holds, so the ranges may be served (RFC 9110
716    /// section 13.1.5): a tag matches by strong comparison (a weak tag never
717    /// matches); a date matches when it equals `last_modified` exactly. When
718    /// it does not hold, send the whole representation.
719    ///
720    /// # Examples
721    ///
722    /// ```
723    /// use parse_net::{range::parse_if_range, EntityTag};
724    ///
725    /// let current = EntityTag::strong(b"v1")?;
726    /// assert!(parse_if_range(b"\"v1\"")?.matches(Some(&current), None));
727    /// assert!(!parse_if_range(b"W/\"v1\"")?.matches(Some(&current), None));
728    /// # Ok::<(), parse_net::ParseError>(())
729    /// ```
730    #[must_use]
731    pub fn matches(
732        &self,
733        current_tag: Option<&EntityTag<'_>>,
734        last_modified: Option<HttpDate>,
735    ) -> bool {
736        match self {
737            IfRange::Tag(t) => current_tag.is_some_and(|c| t.strong_eq(c)),
738            IfRange::Date(d) => last_modified == Some(*d),
739        }
740    }
741}
742
743/// Parses `If-Range`: an entity tag when the value starts with `W/` or `"`,
744/// an IMF-fixdate otherwise.
745///
746/// # Errors
747///
748/// The errors of [`EntityTag::parse`] or [`HttpDate::parse_imf_fixdate`].
749///
750/// # Examples
751///
752/// ```
753/// use parse_net::{range::parse_if_range, IfRange};
754///
755/// assert!(matches!(parse_if_range(b"\"abc\"")?, IfRange::Tag(t) if t.opaque() == b"abc"));
756/// assert!(matches!(parse_if_range(b"Sun, 06 Nov 1994 08:49:37 GMT")?, IfRange::Date(_)));
757/// assert!(parse_if_range(b"tomorrow").is_err());
758/// # Ok::<(), parse_net::ParseError>(())
759/// ```
760pub fn parse_if_range(value: &[u8]) -> Result<IfRange<'_>> {
761    let v = trim_ows(value);
762    if v.starts_with(b"W/") || v.starts_with(b"\"") {
763        EntityTag::parse(value).map(IfRange::Tag)
764    } else {
765        HttpDate::parse_imf_fixdate(value).map(IfRange::Date)
766    }
767}
768
769#[cfg(test)]
770mod tests {
771    use super::*;
772
773    fn resolve_str(
774        v: &[u8],
775        length: u64,
776        coalesce: bool,
777    ) -> (RangeOutcome, std::vec::Vec<ByteRange>) {
778        let mut out = [ByteRange { start: 0, end: 0 }; 32];
779        let h = parse(v, &Limits::DEFAULT).unwrap();
780        let o = resolve(&h, length, coalesce, &Limits::DEFAULT, &mut out);
781        let n = if let RangeOutcome::Ranges(n) = o {
782            n
783        } else {
784            0
785        };
786        (o, out[..n].to_vec())
787    }
788
789    #[test]
790    fn test_range_parse_edge_cases() {
791        let l = Limits::DEFAULT;
792        let k = |v: &[u8]| parse(v, &l).unwrap_err().kind();
793        assert!(parse(b"bytes=0-0", &l).is_ok());
794        assert!(parse(b"BYTES=0-1", &l).unwrap().is_bytes());
795        assert_eq!(k(b"bytes=5-4"), ErrorKind::InvalidRange);
796        assert_eq!(k(b"bytes = 0-1"), ErrorKind::InvalidRange);
797        assert_eq!(k(b"bytes= 0-1"), ErrorKind::InvalidRange);
798        assert_eq!(k(b"bytes=0 -1"), ErrorKind::InvalidNumber);
799        assert_eq!(k(b"bytes="), ErrorKind::InvalidRange);
800        assert_eq!(k(b"bytes=-"), ErrorKind::InvalidRange);
801        assert_eq!(
802            k(b"bytes=0-18446744073709551616"),
803            ErrorKind::NumberOverflow
804        );
805        assert!(parse(b"bytes=0-18446744073709551615", &l).is_ok());
806        assert!(parse(b"bytes=0-1,", &l).is_ok());
807        assert_eq!(k(b"=0-1"), ErrorKind::NotToken);
808        assert_eq!(k(b"bytes"), ErrorKind::InvalidRange);
809        assert!(parse(b"items=0-5", &l).is_ok());
810        assert_eq!(k(b"items=a b"), ErrorKind::InvalidRange);
811        assert_eq!(parse(b"items=0-5", &l).unwrap().specs().count(), 0);
812    }
813
814    #[test]
815    fn test_range_resolution() {
816        assert_eq!(
817            resolve_str(b"bytes=0-", 10, false).1,
818            [ByteRange { start: 0, end: 9 }]
819        );
820        assert_eq!(
821            resolve_str(b"bytes=-0", 10, false).0,
822            RangeOutcome::Unsatisfiable
823        );
824        assert_eq!(
825            resolve_str(b"bytes=-1", 0, false).0,
826            RangeOutcome::Unsatisfiable
827        );
828        assert_eq!(
829            resolve_str(b"bytes=-20", 10, false).1,
830            [ByteRange { start: 0, end: 9 }]
831        );
832        assert_eq!(
833            resolve_str(b"bytes=5-100", 10, false).1,
834            [ByteRange { start: 5, end: 9 }]
835        );
836        assert_eq!(
837            resolve_str(b"bytes=10-", 10, false).0,
838            RangeOutcome::Unsatisfiable
839        );
840        assert_eq!(
841            resolve_str(b"bytes=10-, 0-0", 10, false).1,
842            [ByteRange { start: 0, end: 0 }]
843        );
844        assert_eq!(
845            resolve_str(b"bytes=0-18446744073709551615", u64::MAX, false).1[0].len(),
846            u64::MAX
847        );
848    }
849
850    /// `bytes=0-1,10-11,...` with `n` ranges.
851    fn many(n: u64) -> std::vec::Vec<u8> {
852        use std::fmt::Write as _;
853        let mut text = std::string::String::from("bytes=");
854        for i in 0..n {
855            let _ = write!(text, "{}-{},", i * 10, i * 10 + 1);
856        }
857        text.into_bytes()
858    }
859
860    #[test]
861    fn test_range_17_ranges_ignored() {
862        assert_eq!(
863            resolve_str(&many(16), 1000, false).0,
864            RangeOutcome::Ranges(16)
865        );
866        assert_eq!(resolve_str(&many(17), 1000, false).0, RangeOutcome::Ignore);
867    }
868
869    #[test]
870    fn test_range_three_overlapping_ignored() {
871        assert_eq!(
872            resolve_str(b"bytes=0-10, 5-15", 100, false).0,
873            RangeOutcome::Ranges(2)
874        );
875        assert_eq!(
876            resolve_str(b"bytes=0-10, 5-15, 8-20", 100, false).0,
877            RangeOutcome::Ignore
878        );
879        assert_eq!(
880            resolve_str(b"bytes=50-60, 40-45, 30-35, 20-25", 100, false).0,
881            RangeOutcome::Ignore
882        );
883        assert_eq!(
884            resolve_str(b"bytes=50-60, 40-45, 30-35", 100, false).0,
885            RangeOutcome::Ranges(3)
886        );
887    }
888
889    #[test]
890    fn test_range_coalesce() {
891        let (o, r) = resolve_str(b"bytes=40-49, 0-10, 11-20, 5-6", 100, true);
892        assert_eq!(o, RangeOutcome::Ranges(2));
893        assert_eq!(
894            r,
895            [
896                ByteRange { start: 0, end: 20 },
897                ByteRange { start: 40, end: 49 }
898            ]
899        );
900    }
901
902    #[test]
903    fn test_content_range_cases() {
904        assert!(parse_content_range(b"bytes 0-0/1").is_ok());
905        assert_eq!(
906            parse_content_range(b"bytes 0-0/*").unwrap(),
907            ContentRange::Satisfied {
908                range: ByteRange { start: 0, end: 0 },
909                complete: None
910            }
911        );
912        assert_eq!(
913            parse_content_range(b"bytes */0").unwrap(),
914            ContentRange::Unsatisfied { complete: 0 }
915        );
916        for bad in [
917            &b"bytes 1-0/5"[..],
918            b"bytes 0-5/5",
919            b"bytes0-1/2",
920            b"items 0-1/2",
921            b"bytes 0-1",
922            b"bytes -1/2",
923            b"bytes */*",
924        ] {
925            assert!(
926                parse_content_range(bad).is_err(),
927                "{}",
928                std::string::String::from_utf8_lossy(bad)
929            );
930        }
931    }
932
933    #[test]
934    fn test_content_range_writers() {
935        let mut buf = [0u8; 64];
936        let mut out = crate::SliceSink::new(&mut buf);
937        assert_eq!(
938            write_content_range(ByteRange { start: 5, end: 4 }, None, &mut out),
939            Err(WriteError::OutOfRange)
940        );
941        assert_eq!(
942            write_content_range(ByteRange { start: 0, end: 5 }, Some(5), &mut out),
943            Err(WriteError::OutOfRange)
944        );
945        write_content_range(ByteRange { start: 0, end: 4 }, None, &mut out).unwrap();
946        assert_eq!(out.written(), b"bytes 0-4/*");
947        assert_eq!(
948            parse_content_range(out.written()).unwrap(),
949            ContentRange::Satisfied {
950                range: ByteRange { start: 0, end: 4 },
951                complete: None
952            }
953        );
954        let mut small = [0u8; 8];
955        let mut out = crate::SliceSink::new(&mut small);
956        assert_eq!(write_unsatisfied(1, &mut out), Err(WriteError::Full));
957        assert!(out.is_empty());
958    }
959
960    #[test]
961    fn test_if_range() {
962        let d = HttpDate::from_unix(784_111_777).unwrap();
963        let date = parse_if_range(b"Sun, 06 Nov 1994 08:49:37 GMT").unwrap();
964        assert!(date.matches(None, Some(d)));
965        assert!(!date.matches(None, None));
966        assert!(parse_if_range(b"Sunday, 06-Nov-94 08:49:37 GMT").is_err());
967        let t = EntityTag::strong(b"a").unwrap();
968        assert!(!parse_if_range(b"\"b\"").unwrap().matches(Some(&t), Some(d)));
969    }
970}