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