//! Convert a fixed-size in-memory buffer into a stream of JSON lexical tokens.

#[cfg(feature = "num")]
use crate::lexical::{NumError, parse_f64_result, parse_int_err};
use crate::{
    Pos,
    lexical::{
        self, state, {Analyzer, ErrorKind, Token, Unescaped},
    },
    syntax,
};
use alloc::{string::String, sync::Arc, vec::Vec};
#[cfg(feature = "num")]
use core::str::FromStr;
use core::{
    cmp::Ordering,
    fmt,
    ops::{Deref, Range},
};

#[derive(Clone, Debug)]
struct Ref<B> {
    buf: Arc<B>,
    rng: Range<usize>,
}

impl<B: Deref<Target = [u8]>> Ref<B> {
    fn new(buf: Arc<B>, rng: Range<usize>) -> Ref<B> {
        Self { buf, rng }
    }

    fn as_str(&self) -> &str {
        unsafe { core::str::from_utf8_unchecked(&self.buf[self.rng.start..self.rng.end]) }
    }
}

const INLINE_LEN: usize = 30;

type InlineBuf = [u8; INLINE_LEN];

#[derive(Clone, Debug)]
enum InnerContent<B: Deref<Target = [u8]>> {
    Static(&'static str),
    Inline(u8, InlineBuf),
    NotEscaped(Ref<B>),
    Escaped(Ref<B>),
}

/// Text content of a JSON token identified by a [`FixedAnalyzer`].
///
/// See the [`lexical::Content`] trait, implemented by this struct, for detailed conceptual
/// documentation.
#[derive(Clone, Debug)]
pub struct Content<B: Deref<Target = [u8]> + fmt::Debug>(InnerContent<B>);

impl Content<Vec<u8>> {
    /// Constructs a new `Content` value from a static lifetime string that contains a valid JSON
    /// token.
    ///
    /// The entire string must be a single valid lexical token. It may be the empty string, which is
    /// equivalent to `Token::Eof`; or may be all whitespace, equivalent to [`Token::White`]. String
    /// token content must begin and end with a double quote character.
    ///
    /// # Panics
    ///
    /// Panics if the string is not valid JSON token content.
    ///
    /// # Example
    ///
    /// ```
    /// # use bufjson::lexical::fixed::Content;
    /// let obj_begin = Content::from_static("{");
    /// assert_eq!("{", obj_begin.literal());
    /// assert!(!obj_begin.is_escaped());
    ///
    /// let str_escaped = Content::from_static(r#""hello\nworld""#);
    /// assert_eq!(r#""hello\nworld""#, str_escaped.literal());
    /// assert!(str_escaped.is_escaped());
    /// assert_eq!(r#""hello
    /// world""#, str_escaped.unescaped());
    /// ```
    pub fn from_static(s: &'static str) -> Self {
        let b = s.as_bytes();
        match state::Machine::verify_static(b) {
            false => Self(InnerContent::Static(s)),
            true => Self(InnerContent::Escaped(Ref::new(
                Arc::new(b.to_vec()),
                0..b.len(),
            ))),
        }
    }
}

impl<B: Deref<Target = [u8]> + fmt::Debug> Content<B> {
    /// Returns the literal content of the token exactly as it appears in the JSON text.
    ///
    /// This is an inherent implementation of [`lexical::Content::literal`] for convenience, so it
    /// is available even when you don't have the trait imported. Refer to the trait documentation
    /// for conceptual details.
    #[inline]
    pub fn literal(&self) -> &str {
        match &self.0 {
            InnerContent::Static(s) => s,
            InnerContent::Inline(len, buf) => Self::inline_str(*len, buf),
            InnerContent::NotEscaped(r) | InnerContent::Escaped(r) => r.as_str(),
        }
    }

    /// Returns the number of bytes in the [`literal`] value.
    ///
    /// [`literal`]: method@Self::literal
    #[inline]
    pub fn literal_len(&self) -> usize {
        match &self.0 {
            InnerContent::Static(s) => s.len(),
            InnerContent::Inline(len, _) => *len as usize,
            InnerContent::NotEscaped(r) | InnerContent::Escaped(r) => r.rng.end - r.rng.start,
        }
    }

    /// Indicates whether the token content contains escape sequences.
    ///
    /// This is an inherent implementation of [`lexical::Content::is_escaped`] for convenience, so
    /// it is available even when you don't have the trait imported. Refer to the trait
    /// documentation for conceptual details.
    #[inline(always)]
    pub fn is_escaped(&self) -> bool {
        matches!(self.0, InnerContent::Escaped(_))
    }

    /// Returns a normalized version of [`literal`]` with all escape sequences in the JSON text
    /// fully expanded.
    ///
    /// This is an inherent implementation of [`lexical::Content::unescaped`] for convenience, so
    /// it is available even when you don't have the trait imported. Refer to the trait
    /// documentation for conceptual details.
    ///
    /// # Performance considerations
    ///
    /// - If this content belongs to a non-string token, or a string token that contains no escape
    ///   sequences, does not allocate, and simply returns an [`Unescaped::Literal`] wrapping the
    ///   borrow returned by [`literal`], which is a reference to the internals of this content.
    /// - If this content belongs to a string token containing at least one escape sequence,
    ///   allocates a new owned string value containing the unescaped string content and returns it
    ///   wrapped in [`Unescaped::Expanded`].
    ///
    /// [`literal`]: method@Self::literal
    pub fn unescaped(&self) -> Unescaped<&str> {
        match &self.0 {
            InnerContent::Static(s) => Unescaped::Literal(s),
            InnerContent::Inline(len, buf) => Unescaped::Literal(Self::inline_str(*len, buf)),
            InnerContent::NotEscaped(r) => Unescaped::Literal(r.as_str()),
            InnerContent::Escaped(r) => {
                let mut buf = Vec::new();
                lexical::unescape(r.as_str(), &mut buf);

                // SAFETY: `r` was valid UTF-8 before it was de-escaped, and the de-escaping process
                //         maintains UTF-8 safety.
                let s = unsafe { String::from_utf8_unchecked(buf) };

                Unescaped::Expanded(s)
            }
        }
    }

    /// Compares the token content to another string, expanding embedded escape sequences.
    ///
    /// For non-string tokens, and string tokens for which [`is_escaped`] returns `false`, this
    /// method returns the equivalent of `self.literal().cmp(other)`.
    ///
    /// For string tokens with one or more escape sequences, this method compares a normalized
    /// version of the string value with escape sequences expanded to `other`. It gives the same
    /// result as `self.unescaped().cmp(other)`, but is faster because it avoids the accompanying
    /// allocations and copying.
    ///
    /// Note two important details. First, no unescaping is done on `other`, which is assumed to
    /// contain a string with escape sequences fully expanded. Second, apart from expanding escape
    /// sequences, `self` is compared byte-for-byte against `other`, so if `self` contains the
    /// content of a string token, then `other` will need to begin and end with a quotation mark
    /// character, `"` (U+0022) in order to compare equal.
    ///
    /// # Performance considerations
    ///
    /// Does not allocate.
    ///
    /// Since this method is marginally less efficient than comparing directly against the literal
    /// value, it is preferable to use it only when `is_escaped` returns `true`.
    ///
    /// # Example
    ///
    /// ```
    /// use bufjson::lexical::{Token, fixed::FixedAnalyzer};
    /// use std::cmp::Ordering;
    ///
    /// let mut an = FixedAnalyzer::new(&br#""hello\nworld! \ud83c\udf0d""#[..]);
    ///
    /// assert_eq!(Token::Str, an.next());
    /// assert_eq!(
    ///     Ordering::Equal, an.content().unescaped_cmp(r#""hello
    /// world! 🌍""#));
    /// ```
    ///
    /// [`is_escaped`]: method@Self::is_escaped
    pub fn unescaped_cmp(&self, other: &str) -> Ordering {
        let mut a = self.literal().as_bytes();
        let mut b = other.as_bytes();
        loop {
            match a.iter().position(|&b| b == b'\\') {
                None => return a.cmp(b),
                Some(i) => {
                    let o = a[..i].cmp(&b[..i.min(b.len())]);
                    if o != Ordering::Equal {
                        return o;
                    } else if i >= b.len() {
                        return Ordering::Greater;
                    }
                    let y = b[i];
                    match lexical::unescape_byte(a[i + 1]) {
                        lexical::UnescapeByte::Byte(x) if x != y => return x.cmp(&y),
                        lexical::UnescapeByte::Byte(_) => {
                            a = &a[i + 2..];
                            b = &b[i + 1..];
                        }
                        lexical::UnescapeByte::Unicode => {
                            a = &a[i + 2..];
                            let (x, n) = lexical::unescape_unicode(&mut a);
                            let n = n as usize;
                            let o = x[..n].cmp(&b[i..i + n.min(b.len() - i)]);
                            if o != Ordering::Equal {
                                return o;
                            }
                            b = &b[i + n..];
                        }
                    }
                }
            }
        }
    }

    /// Converts the token content to a 64-bit signed integer.
    ///
    /// This is an inherent implementation of [`lexical::Content::parse_i64`] for convenience, so it
    /// is available even when you don't have the trait imported.
    ///
    /// # Performance considerations
    ///
    /// This implementation is faster than [`lexical::parse_i64`] because it is optimized for the
    /// fixed-size buffer case and does not need to handle the possibility that the number text
    /// might be split across multiple buffers. It never allocates or copies.
    ///
    /// # Example
    ///
    /// ```
    /// use bufjson::lexical::{Token, fixed::FixedAnalyzer};
    ///
    /// let mut an = FixedAnalyzer::new(&b"[123]"[..]);
    ///
    /// assert_eq!(Token::ArrBegin, an.next());
    /// assert_eq!(Token::Num, an.next());
    /// assert_eq!(Ok(123), an.content().parse_i64());
    /// assert_eq!(Token::ArrEnd, an.next());
    /// ```
    #[cfg(feature = "num")]
    #[inline]
    pub fn parse_i64(&self) -> Result<i64, NumError> {
        let chunk = self.literal().as_bytes();

        parse_int!(one_slice, chunk, i64, 1, i64::MIN)
    }

    /// Converts the token content to a 64-bit unsigned integer.
    ///
    /// This is an inherent implementation of [`lexical::Content::parse_u64`] for convenience, so it
    /// is available even when you don't have the trait imported.
    ///
    /// # Performance considerations
    ///
    /// This implementation is faster than [`lexical::parse_u64`] because it is optimized for the
    /// fixed-size buffer case and does not need to handle the possibility that the number text
    /// might be split across multiple buffers. It never allocates or copies.
    ///
    /// # Example
    ///
    /// ```
    /// use bufjson::lexical::{Token, fixed::FixedAnalyzer};
    ///
    /// let mut an = FixedAnalyzer::new(&b"[123]"[..]);
    ///
    /// assert_eq!(Token::ArrBegin, an.next());
    /// assert_eq!(Token::Num, an.next());
    /// assert_eq!(Ok(123), an.content().parse_u64());
    /// assert_eq!(Token::ArrEnd, an.next());
    /// ```
    #[cfg(feature = "num")]
    #[inline]
    pub fn parse_u64(&self) -> Result<u64, NumError> {
        let chunk = self.literal().as_bytes();

        parse_int!(one_slice, chunk, u64, 0, u64::MAX)
    }

    /// Converts the token content to a 128-bit signed integer.
    ///
    /// This is an inherent implementation of [`lexical::Content::parse_i128`] for convenience, so
    /// it is available even when you don't have the trait imported.
    ///
    /// # Performance considerations
    ///
    /// This method is faster than [`lexical::parse_i128`] because it is optimized for the
    /// fixed-size buffer case and does not need to handle the possibility that the number text
    /// might be split across multiple buffers. It never allocates or copies.
    ///
    /// # Example
    ///
    /// ```
    /// use bufjson::lexical::{Token, fixed::FixedAnalyzer};
    ///
    /// const TEXT: &str = r#"{"10^38":100000000000000000000000000000000000000}"#;
    ///
    /// let mut an = FixedAnalyzer::new(TEXT.as_bytes());
    ///
    /// assert_eq!(Token::ObjBegin, an.next());
    /// assert_eq!(Token::Str, an.next());
    /// assert_eq!(Token::NameSep, an.next());
    /// assert_eq!(Token::Num, an.next());
    /// assert_eq!(Ok(10_i128.pow(38)), an.content().parse_i128());
    /// assert_eq!(Token::ObjEnd, an.next());
    /// ```
    #[cfg(feature = "num_ext")]
    #[inline]
    pub fn parse_i128(&self) -> Result<i128, NumError> {
        let chunk = self.literal().as_bytes();

        parse_int!(one_slice, chunk, i128, 1, i128::MIN)
    }

    /// Converts the token content to a 64-bit IEEE double precision floating point number.
    ///
    /// This is an inherent implementation of [`lexical::Content::parse_f64`] for convenience, so it
    /// is available even when you don't have the trait imported.
    ///
    /// # Performance considerations
    ///
    /// This method is faster than [`lexical::parse_f64`] because it is optimized for the fixed-size
    /// buffer case and does not need to handle the possibility that the number text might be split
    /// across multiple buffers. It never allocates or copies.
    ///
    /// # Example
    ///
    /// ```
    /// use bufjson::lexical::{Token, fixed::FixedAnalyzer};
    ///
    /// let mut an = FixedAnalyzer::new(&br#"{"pi":3.14159}"#[..]);
    ///
    /// assert_eq!(Token::ObjBegin, an.next());
    /// assert_eq!(Token::Str, an.next());
    /// assert_eq!(Token::NameSep, an.next());
    /// assert_eq!(Token::Num, an.next());
    /// assert_eq!(Ok(3.14159), an.content().parse_f64());
    /// assert_eq!(Token::ObjEnd, an.next());
    /// ```
    #[cfg(feature = "num")]
    #[inline]
    pub fn parse_f64(&self) -> Result<f64, NumError> {
        parse_f64_result(f64::from_str(self.literal()))
    }
}

impl<B: Deref<Target = [u8]> + fmt::Debug> Content<B> {
    fn from_buf(buf: &Arc<B>, r: Range<usize>, escaped: bool) -> Self {
        debug_assert!(r.start <= r.end);
        debug_assert!(r.end <= buf.len());

        let len = r.end - r.start;

        if len <= INLINE_LEN && !escaped {
            let mut inner: InlineBuf = [0; INLINE_LEN];
            inner[..len].copy_from_slice(&buf[r]);

            Self(InnerContent::Inline(len as u8, inner))
        } else {
            let r = Ref::new(Arc::clone(buf), r);

            Self(if !escaped {
                InnerContent::NotEscaped(r)
            } else {
                InnerContent::Escaped(r)
            })
        }
    }

    fn inline_str(len: u8, buf: &InlineBuf) -> &str {
        unsafe { core::str::from_utf8_unchecked(&buf[0..len as usize]) }
    }
}

impl Default for Content<Vec<u8>> {
    fn default() -> Self {
        Self(InnerContent::Static("")) // Equivalent to EOF
    }
}

impl<B: Deref<Target = [u8]> + fmt::Debug> fmt::Display for Content<B> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(self.literal())
    }
}

impl<B: Deref<Target = [u8]> + fmt::Debug> super::Content for Content<B> {
    type Literal<'a>
        = &'a str
    where
        Self: 'a;

    #[inline(always)]
    fn literal<'a>(&'a self) -> Self::Literal<'a> {
        Content::literal(self)
    }

    #[inline(always)]
    fn literal_len(&self) -> usize {
        Content::literal_len(self)
    }

    #[inline(always)]
    fn is_escaped(&self) -> bool {
        Content::is_escaped(self)
    }

    #[inline(always)]
    fn unescaped<'a>(&'a self) -> Unescaped<Self::Literal<'a>> {
        Content::unescaped(self)
    }

    #[inline(always)]
    fn unescaped_cmp(&self, other: &str) -> Ordering {
        Content::unescaped_cmp(self, other)
    }

    #[cfg(feature = "num")]
    #[inline(always)]
    fn parse_i64(&self) -> Result<i64, NumError> {
        Content::parse_i64(self)
    }

    #[cfg(feature = "num")]
    #[inline(always)]
    fn parse_u64(&self) -> Result<u64, NumError> {
        Content::parse_u64(self)
    }

    #[cfg(feature = "num_ext")]
    #[inline(always)]
    fn parse_i128(&self) -> Result<i128, NumError> {
        Content::parse_i128(self)
    }

    #[cfg(feature = "num")]
    #[inline(always)]
    fn parse_f64(&self) -> Result<f64, NumError> {
        Content::parse_f64(self)
    }
}

// Assert that `Content` does not grow beyond 32 bytes (four 64-bit words).
#[cfg(target_pointer_width = "64")]
const _: [(); 32] = [(); core::mem::size_of::<Content<Vec<u8>>>()];

/// Lexical analysis error detected by a [`FixedAnalyzer`].
///
/// See the [`lexical::Error`] trait, implemented by this struct, for further documentation.
#[derive(Copy, Clone, Debug)]
pub struct Error {
    kind: ErrorKind,
    pos: Pos,
}

impl Error {
    /// Returns the category of error.
    ///
    /// This is an inherent implementation of [`lexical::Error::kind`] for convenience, so it is
    /// available even when you don't have the trait imported.
    #[inline(always)]
    pub fn kind(&self) -> ErrorKind {
        self.kind
    }

    /// Returns the position in the JSON text where the error was encountered.
    ///
    /// This is an inherent implementation of [`lexical::Error::pos`] for convenience, so it is
    /// available even when you don't have the trait imported.
    #[inline(always)]
    pub fn pos(&self) -> &Pos {
        &self.pos
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.kind.fmt_at(f, Some(&self.pos))
    }
}

impl core::error::Error for Error {}

impl lexical::Error for Error {
    #[inline(always)]
    fn kind(&self) -> ErrorKind {
        Error::kind(self)
    }

    #[inline(always)]
    fn pos(&self) -> &Pos {
        Error::pos(self)
    }
}

#[derive(Debug)]
struct StoredContent {
    len: usize,
    escaped: bool,
}

impl StoredContent {
    #[inline(always)]
    fn new(len: usize, escaped: bool) -> Self {
        Self { len, escaped }
    }
}

impl Default for StoredContent {
    fn default() -> Self {
        Self::new(0, false)
    }
}

/// A [`lexical::Analyzer`] to tokenize fixed buffers of JSON text.
///
/// Use `FixedAnalyzer` for zero-allocation, low-copy, stream-oriented lexical analysis of any
/// pre-allocated fixed-size buffer of JSON text.
///
/// As with any [`lexical::Analyzer`] implementation, you can construct a [`syntax::Parser`] from a
/// `FixedAnalyzer` to unlock richer stream-oriented syntactic analysis while retaining low overhead
/// guarantees of the underlying lexical analyzer.
///
/// # Performance considerations
///
/// The [`next`] method never allocates or copies and has very low overhead, above and beyond just
/// examining the bytes of the next token in the buffer, for doing state transitions and remembering
/// state.
///
/// The [`content`] method never allocates. For punctuation and literal tokens, it never copies. For
/// number and string tokens, it may copy if the token is very short; otherwise, it just returns a
/// reference-counted slice of the internal buffer that was passed to [`new`].
///
/// It should be noted that the `Content` structure returned by [`content`] is somewhat "fat", at 32
/// bytes; it is preferable not to fetch it for tokens where the content is either statically
/// knowable (literals and punctuation) or not required (*e.g.*, whitespace in some applications).
///
/// [Unescaping][`lexical::Content::unescaped`] a [`Content`] value that contains an escaped string
/// token always allocates; but calling `unescaped` on a `Content` value that does not contain any
/// escape sequences is a no-op that neither allocates nor does any other work.
///
/// # Memory considerations
///
/// Because the [`Content`] for longer string and number tokens directly reference slices of the
/// buffer, the buffer will not be dropped until all of these [`Content`] values are dropped.
///
/// # Examples
///
/// Scan a static string slice into tokens:
///
/// ```
/// use bufjson::lexical::{Token, fixed::FixedAnalyzer};
///
/// let mut lexer = FixedAnalyzer::new(r#"[123, "abc"]"#.as_bytes());
///
/// assert_eq!(Token::ArrBegin, lexer.next());
/// assert_eq!(Token::Num, lexer.next());
/// assert_eq!(Token::ValueSep, lexer.next());
/// assert_eq!(Token::White, lexer.next());
/// assert_eq!(Token::Str, lexer.next());
/// assert_eq!(Token::ArrEnd, lexer.next());
/// assert_eq!(Token::Eof, lexer.next());
/// ```
///
/// Create a parser wrapping a `FixedAnalyzer` that scans a `Vec<u8>`.
///
/// ```
/// use bufjson::{lexical::{Token, fixed::FixedAnalyzer}, syntax::Parser};
///
/// let vec = b"  {\"flag\": true}".to_vec();
/// let mut parser = FixedAnalyzer::new(vec).into_parser();
///
/// assert_eq!(Token::ObjBegin, parser.next_meaningful());
/// assert_eq!(Token::Str, parser.next_meaningful());
/// assert_eq!(Token::LitTrue, parser.next_meaningful());
/// assert_eq!(Token::ObjEnd, parser.next_meaningful());
/// assert_eq!(Token::Eof, parser.next_meaningful());
/// ```
///
/// [`syntax::Parser`]: crate::syntax::Parser
/// [`new`]: method@Self::new
/// [`next`]: method@Self::next
/// [`content`]: method@Self::content
pub struct FixedAnalyzer<B: Deref<Target = [u8]> + fmt::Debug> {
    buf: Arc<B>,
    content: StoredContent,
    err: Option<Error>,
    mach: state::Machine<state::DerefBuf<B, Arc<B>>>,
    pos: Pos,
}

impl<B: Deref<Target = [u8]> + fmt::Debug> FixedAnalyzer<B> {
    /// Constructs a new lexer to tokenize a fixed in-memory buffer.
    ///
    /// The buffer can be anything that implements `Deref<Target = [u8]>`, including `&[u8]`,
    /// `Vec<u8>`, `Cow<'_, [u8]>` and data types such as `bytes::Bytes` from the `bytes` crate.
    /// For non-trivial tokens, the token [content][method@Self::content] is referenced directly
    /// into the buffer.
    ///
    /// # Examples
    ///
    /// ```
    /// # use bufjson::lexical::fixed::FixedAnalyzer;
    /// let mut lexer: FixedAnalyzer<Vec<u8>> = FixedAnalyzer::new(vec![b'n', b'u', b'l', b'l']);
    /// ```
    pub fn new(buf: B) -> Self {
        let buf = Arc::new(buf);
        let mach = state::Machine::new(state::DerefBuf::new(Arc::clone(&buf)));
        let content = StoredContent::default();
        let err = None;
        let pos = Pos::default();

        Self {
            buf,
            content,
            err,
            pos,
            mach,
        }
    }

    /// Recognizes the next lexical token in the buffer without allocating or copying.
    ///
    /// This is an inherent implementation of [`lexical::Analyzer::next`] for convenience, so it is
    /// available even when you don't have the trait imported.
    ///
    /// # Example
    ///
    /// ```
    /// # use bufjson::lexical::{Token, fixed::FixedAnalyzer};
    /// let mut lexer = FixedAnalyzer::new(&b"99.9e-1"[..]);
    /// assert_eq!(Token::Num, lexer.next());
    /// assert_eq!(Token::Eof, lexer.next());
    /// assert_eq!(Token::Eof, lexer.next()); // Once EOF is reached, it is returned to infinity.
    /// ```
    #[allow(clippy::should_implement_trait)]
    pub fn next(&mut self) -> Token {
        if self.err.is_some() {
            return Token::Err;
        }

        self.pos = *self.mach.pos();

        match self.mach.next() {
            state::Next::Done(token, escaped, n) => {
                self.content = StoredContent::new(n, escaped);

                token
            }
            state::Next::Part(token, n) => match self.mach.end() {
                state::End::Done => {
                    self.content = StoredContent::new(n, false);

                    token
                }
                state::End::Nil => unreachable!(),
                state::End::Err => {
                    let kind = self.mach.err_kind().expect("there should be an error kind");
                    let pos = *self.mach.pos();
                    self.err = Some(Error { kind, pos });

                    Token::Err
                }
            },
            state::Next::Nil => {
                self.content = StoredContent::default();

                Token::Eof
            }
            state::Next::Err(_) => {
                let kind = self.mach.err_kind().expect("there should be an error kind");
                let pos = *self.mach.pos();
                self.err = Some(Error { kind, pos });

                Token::Err
            }
        }
    }

    /// Fetches the text content of the most recent non-error token without allocating.
    ///
    /// No copying is done except possibly for very small number and string tokens. Larger number
    /// and string tokens directly reference slices of the buffer, without any copying.
    ///
    /// This is an inherent implementation of [`lexical::Analyzer::content`] for convenience, so it
    /// is available even when you don't have the trait imported.
    ///
    /// # Panics
    ///
    /// Panics if the most recent token returned by [`next`] was [`Token::Err`].
    ///
    /// # Examples
    ///
    /// ```
    /// # use bufjson::lexical::{Token, fixed::FixedAnalyzer};
    /// let mut lexer = FixedAnalyzer::new(&b"  1.0"[..]);
    ///
    /// assert_eq!(Token::White, lexer.next());
    /// assert_eq!("  ", lexer.content().literal());
    ///
    /// assert_eq!(Token::Num, lexer.next());
    /// assert_eq!("1.0", lexer.content().literal());
    /// ```
    ///
    /// [`next`]: method@Self::next
    #[inline]
    pub fn content(&self) -> Content<B> {
        if let Ok(content) = self.try_content() {
            content
        } else {
            panic!("no content: last `next()` returned `Token::Err` (use `err()` instead)");
        }
    }

    /// Fetches the error value associated with the most recent error token.
    ///
    /// No copying or allocation is done.
    ///
    /// This is an inherent implementation of [`lexical::Analyzer::err`] for convenience, so it is
    /// available even when you don't have the trait imported.
    ///
    /// # Panics
    ///
    /// Panics if the most recent token returned by [`next`] was not [`Token::Err`].
    ///
    /// # Examples
    ///
    /// ```
    /// use bufjson::lexical::{ErrorKind, Expect, Token, fixed::FixedAnalyzer};
    ///
    /// let mut lexer = FixedAnalyzer::new(&b"error!"[..]);
    ///
    /// assert_eq!(Token::Err, lexer.next());
    /// assert!(matches!(
    ///     lexer.err().kind(),
    ///     ErrorKind::UnexpectedByte { token: None, expect: Expect::TokenStartChar, actual: b'e'}
    /// ));
    /// ```
    ///
    /// [`next`]: method@Self::next
    #[inline]
    pub fn err(&self) -> Error {
        if let Err(err) = self.try_content() {
            err
        } else {
            panic!("no error: last `next()` did not return `Token::Err` (use `content()` instead)");
        }
    }

    /// Returns the position of the start of the token most recently scanned by [`next`].
    ///
    /// This is an inherent implementation of [`lexical::Analyzer::pos`] for convenience, so it is
    /// available even when you don't have the trait imported.
    ///
    /// # Examples
    ///
    /// Before any token is scanned, the position is the default position.
    ///
    /// ```
    /// # use bufjson::{Pos, lexical::fixed::FixedAnalyzer};
    /// assert_eq!(Pos::default(), *FixedAnalyzer::new(&b""[..]).pos());
    /// ```
    ///
    /// The position of the first token returned is always the start of the buffer.
    ///
    /// ```
    /// use bufjson::{Pos, lexical::{Token, fixed::FixedAnalyzer}};
    ///
    /// let mut lexer = FixedAnalyzer::new(&b" \n"[..]);
    ///
    /// // Read the two-byte whitespace token that starts at offset 0.
    /// assert_eq!(Token::White, lexer.next());
    /// assert_eq!(Pos::default(), *lexer.pos());
    ///
    /// // The EOF token starts at the end of the whitespace token.
    /// assert_eq!(Token::Eof, lexer.next());
    /// assert_eq!(Pos { offset: 2, line: 2, col: 1}, *lexer.pos());
    /// ```
    ///
    /// On errors, the position reported by `pos` may be different from the position reported by the
    /// error returned from [`err`]. This is because the `pos` indicates the start of the token
    /// where the error occurred, and the error position is the exact position of the error.
    ///
    /// ```
    /// use bufjson::{Pos, lexical::{Token, fixed::FixedAnalyzer}};
    ///
    /// let mut lexer = FixedAnalyzer::new(&b"123_"[..]);
    ///
    /// assert_eq!(Token::Err, lexer.next());
    /// // `pos` is at the start of the number token that has the problem...
    /// assert_eq!(Pos::default(), *lexer.pos());
    /// // ...but the error contains the exact problem position: offset 3, column 4.
    /// assert_eq!(Pos { offset: 3, line: 1, col: 4 }, *lexer.err().pos())
    /// ```
    ///
    /// [`next`]: method@Self::next
    /// [`err`]: method@Self::err
    #[inline(always)]
    pub fn pos(&self) -> &Pos {
        &self.pos
    }

    /// Fetches the content or error associated with the most recent token, without allocating.
    ///
    /// This is an inherent implementation of [`lexical::Analyzer::try_content`] for convenience, so
    /// it is available even when you don't have the trait imported.
    ///
    /// # Examples
    ///
    /// An `Ok` value is returned as long as the lexical analyzer isn't in an error state.
    ///
    /// ```
    /// # use bufjson::lexical::{Token, fixed::FixedAnalyzer};
    /// let mut lexer = FixedAnalyzer::new(&b"99.9e-1"[..]);
    /// assert_eq!(Token::Num, lexer.next());
    /// assert!(matches!(lexer.try_content(), Ok(c) if c.literal() == "99.9e-1"));
    /// ```
    ///
    /// Once the lexical analyzer encounters a lexical error, it will return an `Err` value
    /// describing that error.
    ///
    /// ```
    /// use bufjson::{Pos, lexical::{Token, fixed::FixedAnalyzer}};
    ///
    /// let mut lexer = FixedAnalyzer::new(&b"[unquoted]"[..]);
    /// assert_eq!(Token::ArrBegin, lexer.next());
    /// assert_eq!(Token::Err, lexer.next());
    /// assert_eq!(Pos { offset: 1, line: 1, col: 2}, *lexer.try_content().unwrap_err().pos());
    /// ```
    pub fn try_content(&self) -> Result<Content<B>, Error> {
        match self.err {
            None => {
                let offset = self.pos.offset;

                Ok(Content::from_buf(
                    &self.buf,
                    offset..offset + self.content.len,
                    self.content.escaped,
                ))
            }
            Some(err) => Err(err),
        }
    }

    /// Converts a lexical analyzer into a syntax parser, consuming the lexical analyzer in the
    /// process.
    ///
    /// You can convert the parser back into the underlying lexical analyzer using
    /// [`Parser::into_inner`].
    ///
    /// # Examples
    ///
    /// ```
    /// use bufjson::lexical::{Token, fixed::FixedAnalyzer};
    ///
    /// // Create a lexical analyzer and consume the first token.
    /// let mut lexer = FixedAnalyzer::new(&b"true false"[..]);
    /// assert_eq!(Token::LitTrue, lexer.next());
    ///
    /// // Convert the lexer into a parser. Since `true` is consumed, the next meaningful token is
    /// // `false`.
    /// let mut parser = lexer.into_parser();
    /// assert_eq!(Token::LitFalse, parser.next_meaningful());
    /// ```
    ///
    /// [`Parser::into_inner`]: syntax::Parser::into_inner
    pub fn into_parser(self) -> syntax::Parser<FixedAnalyzer<B>> {
        syntax::Parser::new(self)
    }
}

impl<B: Deref<Target = [u8]> + fmt::Debug> Analyzer for FixedAnalyzer<B> {
    type Content = Content<B>;
    type Error = Error;

    #[inline(always)]
    fn next(&mut self) -> Token {
        FixedAnalyzer::next(self)
    }

    #[inline(always)]
    fn try_content(&self) -> Result<Self::Content, Error> {
        FixedAnalyzer::try_content(self)
    }

    #[inline(always)]
    fn pos(&self) -> &Pos {
        FixedAnalyzer::pos(self)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::lexical::Expect;
    use rstest::rstest;
    use std::error::Error as _;

    #[rstest]
    #[case::invalid_char("a")]
    #[case::f("f")]
    #[case::fa("fa")]
    #[case::fal("fal")]
    #[case::fals("fals")]
    #[case::false_leading_space(" false")]
    #[case::false_trailing_space("false ")]
    #[case::n("n")]
    #[case::nu("nu")]
    #[case::nul("nul")]
    #[case::null_leading_space(" null")]
    #[case::null_trailing_space("null ")]
    #[case::t("t")]
    #[case::tr("tr")]
    #[case::tru("tru")]
    #[case::true_leading_space(" true")]
    #[case::true_trailing_space("true ")]
    #[case::num_dot(".")]
    #[case::num_zero_dot("0.")]
    #[case::num_zero_zero("00")]
    #[case::num_leading_space(" 0")]
    #[case::num_trailing_space("0 ")]
    #[case::str_unterminated_0(r#"""#)]
    #[case::str_unterminated_1(r#""a"#)]
    #[case::str_leading_space(r#" "a""#)]
    #[case::str_trailing_space(r#""a" "#)]
    #[should_panic(expected = "invalid JSON content")]
    fn test_content_from_static_panic(#[case] s: &'static str) {
        let c = Content::from_static(s);

        panic!("content unexpectedly created: {c:?}");
    }

    #[rstest]
    #[case::eof("", None)]
    #[case::arr_begin("[", None)]
    #[case::arr_end("]", None)]
    #[case::obj_begin("{", None)]
    #[case::obj_end("}", None)]
    #[case::value_sep(",", None)]
    #[case::name_sep(":", None)]
    #[case::white_space(" ", None)]
    #[case::white_tab("\t", None)]
    #[case::white_lf("\n", None)]
    #[case::white_cr("\r", None)]
    #[case::white_lots("   \t\t   \r  \r\n  \t\n", None)]
    #[case::lit_false("false", None)]
    #[case::lit_null("null", None)]
    #[case::lit_true("true", None)]
    #[case::num_zero("0", None)]
    #[case::num_zero_point_one("0.1", None)]
    #[case::num_123("123", None)]
    #[case::num_neg_zero("-0", None)]
    #[case::num_neg_zero_point_one("-0.1", None)]
    #[case::num_neg_123("-123", None)]
    #[case::num_1e1("1e1", None)]
    #[case::num_1e_plus_1("1e+1", None)]
    #[case::num_1e_minus_1("1e-1", None)]
    #[case::str_empty(r#""""#, None)]
    #[case::str_a(r#""a""#, None)]
    #[case::str_emoji(r#""🤠""#, None)]
    #[case::str_escaped_nul(r#""\u0000""#, Some("\"\u{00}\""))]
    fn test_content_from_static_ok(
        #[case] s: &'static str,
        #[case] unescaped: Option<&'static str>,
    ) {
        let c = Content::from_static(s);

        assert_eq!(s, c.literal());
        assert_eq!(s, c.to_string());
        assert_eq!(s.len(), c.literal_len());
        assert_eq!(s.len(), lexical::Content::literal_len(&c));

        match unescaped {
            Some(u) => assert_eq!(u, c.unescaped()),
            None => assert!(!c.is_escaped()),
        };
    }

    #[rstest]
    #[case::static_a(Content(InnerContent::Static("a")), "a", "a")]
    #[case::inline_empty(Content(inline_buf(b"").into()), "", "")]
    #[case::inline_a_1(Content(inline_buf(b"a").into()), "a", "a")]
    #[case::inline_a_inline_len(Content(inline_buf(&[b'a'; INLINE_LEN]).into()), "a".repeat(INLINE_LEN), "a".repeat(INLINE_LEN))]
    #[case::not_escaped_empty(Content(InnerContent::NotEscaped(Ref::new(Arc::new(b"".to_vec()), 0..0))), "", "")]
    #[case::not_escaped_a(Content(InnerContent::NotEscaped(Ref::new(Arc::new(b"a".to_vec()), 0..1))), "a", "a")]
    #[case::escaped_empty(Content(InnerContent::Escaped(Ref::new(Arc::new(b"".to_vec()), 0..0))), "", "")]
    #[case::escaped_a(Content(InnerContent::Escaped(Ref::new(Arc::new(b"a".to_vec()), 0..1))), "a", "a")]
    #[case::escaped_nl(Content(InnerContent::Escaped(Ref::new(Arc::new(b"\\n".to_vec()), 0..2))), "\\n", "\n")]
    #[case::escaped_nul(Content(InnerContent::Escaped(Ref::new(Arc::new(b"\\u0000".to_vec()), 0..6))), "\\u0000", "\u{0000}")]
    fn test_content(
        #[case] content: Content<Vec<u8>>,
        #[case] literal: impl AsRef<str>,
        #[case] unescaped: impl AsRef<str>,
    ) {
        let literal = literal.as_ref();
        let unescaped = unescaped.as_ref();

        assert_eq!(literal, content.literal());
        assert_eq!(literal, content.to_string());
        assert_eq!(literal.len(), content.literal_len());
        assert_eq!(literal.len(), lexical::Content::literal_len(&content));
        assert_eq!(unescaped, content.unescaped());
        assert_eq!(unescaped, lexical::Content::unescaped(&content));
        assert_eq!(Ordering::Equal, content.unescaped_cmp(unescaped));
    }

    #[rstest]
    #[case::arr_begin("[")]
    #[case::arr_end("]")]
    #[case::empty("")]
    #[case::lit_false("false")]
    #[case::lit_false("null")]
    #[case::lit_false("true")]
    #[case::name_sep(":")]
    #[case::num_0("1")]
    #[case::num_1("1")]
    #[case::num_pi("3.14159")]
    #[case::num_minus_1("-1")]
    #[case::num_big1("1234567890.1234567890")]
    #[case::num_big2("1e100")]
    #[case::num_big3("10.000e+99")]
    #[case::str_empty(r#""""#)]
    #[case::str_a(r#""a""#)]
    #[case::str_utf8_2_bytes(r#""è""#)]
    #[case::str_utf8_3_bytes(r#""€""#)]
    #[case::str_utf8_4_bytes(r#""🇮🇹""#)]
    #[case::str_utf8_all(r#""🇮🇹 Questo caffè costa 3€. ☕""#)]
    #[case::obj_begin("{")]
    #[case::obj_end("}")]
    #[case::value_sep(",")]
    #[case::white1(" ")]
    #[case::white2("\t")]
    #[case::white3("\r")]
    #[case::white4("\n")]
    #[case::white5("   \t\r\n \n\t\t\r \r\n\r")]
    fn test_content_unescaped_cmp_identity(#[case] input: &'static str) {
        let content = Content::from_static(input);

        assert_eq!(
            Ordering::Equal,
            lexical::unescaped_cmp(content.literal(), input)
        );
        assert_eq!(Ordering::Equal, content.unescaped_cmp(input));
        assert_eq!(
            Ordering::Equal,
            lexical::Content::unescaped_cmp(&content, input)
        );
    }

    #[rstest]
    #[case::empty_str_empty("", r#""""#, Ordering::Less)]
    #[case::empty_num("", "1", Ordering::Less)]
    #[case::empty_white("", " ", Ordering::Less)]
    #[case::num_empty("0", "", Ordering::Greater)]
    #[case::str_a_str_b(r#""a""#, r#""b""#, Ordering::Less)]
    #[case::str_a_str_empty(r#""a""#, r#""""#, Ordering::Greater)]
    #[case::str_b_str_a(r#""b""#, r#""a""#, Ordering::Greater)]
    #[case::str_bar_str_bard(r#""bar""#, r#""bard""#, Ordering::Less)]
    #[case::str_bar_str_bark(r#""bar""#, r#""bark""#, Ordering::Less)]
    #[case::str_bard_str_bar(r#""bard""#, r#""bar""#, Ordering::Greater)]
    #[case::str_bard_str_bark(r#""bard""#, r#""bark""#, Ordering::Less)]
    #[case::str_bark_str_bar(r#""bark""#, r#""bar""#, Ordering::Greater)]
    #[case::str_bark_str_bard(r#""bark""#, r#""bard""#, Ordering::Greater)]
    #[case::str_empty_empty(r#""""#, "", Ordering::Greater)]
    #[case::str_empty_str_a(r#""""#, r#""a""#, Ordering::Less)]
    #[case::str_esc_quot_str_quot(r#""\"""#, "\"\"\"", Ordering::Equal)]
    #[case::str_esc_bsol_str_bsol(r#""\\""#, "\"\\\"", Ordering::Equal)]
    #[case::str_esc_sol_str_sol(r#""\/""#, "\"/\"", Ordering::Equal)]
    #[case::str_esc_bs_str_bs(r#""\b""#, "\"\x08\"", Ordering::Equal)]
    #[case::str_esc_ff_str_ff(r#""\f""#, "\"\x0c\"", Ordering::Equal)]
    #[case::str_esc_nl_str_nl(r#""\n""#, "\"\n\"", Ordering::Equal)]
    #[case::str_esc_nl_str_cr(r#""\n""#, "\"\r\"", Ordering::Less)]
    #[case::str_esc_cr_str_cr(r#""\r""#, "\"\r\"", Ordering::Equal)]
    #[case::str_esc_cr_str_nl(r#""\r""#, "\"\n\"", Ordering::Greater)]
    #[case::str_esc_tab_str_tab(r#""\t""#, "\"\t\"", Ordering::Equal)]
    #[case::str_esc_u_0008_str_bs(r#""\u0008""#, "\"\x08\"", Ordering::Equal)]
    #[case::str_esc_u_0009_str_tab(r#""\u0009""#, "\"\t\"", Ordering::Equal)]
    #[case::str_esc_u_000a_str_nl(r#""\u000a""#, "\"\n\"", Ordering::Equal)]
    #[case::str_esc_u_000c_str_ff(r#""\u000c""#, "\"\x0c\"", Ordering::Equal)]
    #[case::str_esc_u_000d_str_cr(r#""\u000d""#, "\"\r\"", Ordering::Equal)]
    #[case::str_esc_u_0022_str_quot(r#""\u0022""#, "\"\"\"", Ordering::Equal)]
    #[case::str_esc_u_002f_str_sol(r#""\u002f""#, "\"/\"", Ordering::Equal)]
    #[case::str_esc_u_005c_str_bsol(r#""\u005c""#, "\"\\\"", Ordering::Equal)]
    #[case::str_esc_u_0062_str_a(r#""\u0063""#, r#""a""#, Ordering::Greater)]
    #[case::str_esc_u_00e8_str_e_grave(r#""\u00e8""#, "\"\u{00e8}\"", Ordering::Equal)]
    #[case::str_esc_u_20ac_str_euro(r#""\u20ac""#, "\"\u{20ac}\"", Ordering::Equal)]
    #[case::str_esc_u_surrogate_str_globe(r#""\ud83c\udf0d""#, "\"\u{1f30d}\"", Ordering::Equal)]
    #[case::str_esc_at_end_of_longer_str(r#""abcde\n""#, r#""abc""#, Ordering::Greater)]
    #[case::str_esc_consecutive(r#""\n\t""#, "\"\n\t\"", Ordering::Equal)]
    #[case::str_where_other_terminates_early(r#""\n""#, "\"", Ordering::Greater)]
    #[case::white_empty(" ", "", Ordering::Greater)]
    #[case::smoke_1(
        r#""Path: C:\\Users\\alice\/docs""#,
        "\"Path: C:\\Users\\alice/docs\"",
        Ordering::Equal
    )]
    #[case::smoke_2(
        r#""At the caff\u00e8 counter, he paid the 10,50\u00a0\u20ac and\nquickly downed the cappucino. Then,\r\n\twith a quick\"ciao\u0022, he turned and walked out.""#,
        "\"At the caffè counter, he paid the 10,50\u{00a0}€ and\nquickly downed the cappucino. Then,\r\n\twith a quick\"ciao\", he turned and walked out.\"",
        Ordering::Equal,
    )]
    #[case::smoke_3(
        r#""At the caff\u00e8 counter, he paid the 10,50\u00a0\u20ac and\nquickly downed the cappucino. Then,\r\n\twith a quick\"ciao\u0022, he turned and ran out.""#,
        "\"At the caffè counter, he paid the 10,50\u{00a0}€ and\nquickly downed the cappucino. Then,\r\n\twith a quick\"ciao\", he turned and walked out.\"",
        Ordering::Less,
    )]
    fn test_unescaped_cmp_ok(#[case] a: &'static str, #[case] b: &str, #[case] expect: Ordering) {
        let content = Content::from_static(a);

        assert_eq!(expect, lexical::unescaped_cmp(content.literal(), b));
        assert_eq!(expect, content.unescaped_cmp(b));
        assert_eq!(expect, lexical::Content::unescaped_cmp(&content, b));
    }

    #[cfg(feature = "num")]
    #[rstest]
    #[case::zero("0", 0)]
    #[case::minus_zero("-0", 0)]
    #[case::one("1", 1)]
    #[case::minus_one("-1", -1)]
    #[case::forty_two("42", 42)]
    #[case::minus_forty_two("-42", -42)]
    #[case::all_digits("9876543210", 9876543210)]
    #[case::max_minus_1(format!("{}", i64::MAX-1), i64::MAX-1)]
    #[case::max(format!("{}", i64::MAX), i64::MAX)]
    #[case::min_plus_1(format!("{}", i64::MIN+1), i64::MIN+1)]
    #[case::min(format!("{}", i64::MIN), i64::MIN)]
    fn test_content_parse_i64_ok(#[case] input: impl AsRef<str>, #[case] expect: i64) {
        let mut an = FixedAnalyzer::new(input.as_ref().as_bytes().to_vec());
        assert_eq!(Token::Num, an.next());
        let content = an.content();
        assert_eq!(Ok(expect), content.parse_i64());
        assert_eq!(Ok(expect), lexical::Content::parse_i64(&content));
    }

    #[cfg(feature = "num")]
    #[rstest]
    #[case::decimal_zero("10.0", NumError::Format)]
    #[case::decimal("3.14159", NumError::Format)]
    #[case::exponent_zero("0e0", NumError::Format)]
    #[case::exponent_positive("10E+1", NumError::Format)]
    #[case::exponent_negative("1E-98", NumError::Format)]
    #[case::range_i64_min_minus_1(format!("{}", i64::MIN as i128 - 1), NumError::Range)]
    #[case::range_i128_min(format!("{}", i128::MIN), NumError::Range)]
    #[case::range_i64_max_plus_1(format!("{}", i64::MAX as i128 + 1), NumError::Range)]
    #[case::range_i128_max(format!("{}", i128::MAX), NumError::Range)]
    fn test_content_parse_i64_err(#[case] input: impl AsRef<str>, #[case] expect: NumError) {
        let mut an = FixedAnalyzer::new(input.as_ref().as_bytes().to_vec());
        assert_eq!(Token::Num, an.next());
        let content = an.content();
        assert_eq!(Err(expect), content.parse_i64());
        assert_eq!(Err(expect), lexical::Content::parse_i64(&content));
    }

    #[cfg(feature = "num")]
    #[rstest]
    #[case::zero("0", 0)]
    #[case::one("1", 1)]
    #[case::forty_two("42", 42)]
    #[case::all_digits("9876543210", 9876543210)]
    #[case::max_minus_1(format!("{}", u64::MAX-1), u64::MAX-1)]
    #[case::max(format!("{}", u64::MAX), u64::MAX)]
    fn test_content_parse_u64_ok(#[case] input: impl AsRef<str>, #[case] expect: u64) {
        let mut an = FixedAnalyzer::new(input.as_ref().as_bytes().to_vec());
        assert_eq!(Token::Num, an.next());
        let content = an.content();
        assert_eq!(Ok(expect), content.parse_u64());
        assert_eq!(Ok(expect), lexical::Content::parse_u64(&content));
    }

    #[cfg(feature = "num")]
    #[rstest]
    #[case::minus_zero("-0", NumError::Format)]
    #[case::minus_one("-1", NumError::Format)]
    #[case::minus_forty_two("-42", NumError::Format)]
    #[case::decimal_zero("10.0", NumError::Format)]
    #[case::decimal("3.14159", NumError::Format)]
    #[case::exponent_zero("0e0", NumError::Format)]
    #[case::exponent_positive("10E+1", NumError::Format)]
    #[case::exponent_negative("1E-98", NumError::Format)]
    #[case::range_u64_max_plus_1(format!("{}", u64::MAX as i128 + 1), NumError::Range)]
    #[case::range_i128_max(format!("{}", i128::MAX), NumError::Range)]
    fn test_content_parse_u64_err(#[case] input: impl AsRef<str>, #[case] expect: NumError) {
        let mut an = FixedAnalyzer::new(input.as_ref().as_bytes().to_vec());
        assert_eq!(Token::Num, an.next());
        let content = an.content();
        assert_eq!(Err(expect), content.parse_u64());
        assert_eq!(Err(expect), lexical::Content::parse_u64(&content));
    }

    #[cfg(feature = "num_ext")]
    #[rstest]
    #[case::zero("0", 0)]
    #[case::minus_zero("-0", 0)]
    #[case::one("1", 1)]
    #[case::minus_one("-1", -1)]
    #[case::forty_two("42", 42)]
    #[case::minus_forty_two("-42", -42)]
    #[case::all_digits("9876543210", 9876543210)]
    #[case::i64_max(format!("{}", i64::MAX), i64::MAX as i128)]
    #[case::i64_min(format!("{}", i64::MIN), i64::MIN as i128)]
    #[case::beyond_i64_max(format!("{}", i64::MAX as i128 + 1), i64::MAX as i128 + 1)]
    #[case::beyond_i64_min(format!("{}", i64::MIN as i128 - 1), i64::MIN as i128 - 1)]
    #[case::pow10_38(format!("{}", 10_i128.pow(38)), 10_i128.pow(38))]
    #[case::neg_pow10_38(format!("{}", -10_i128.pow(38)), -10_i128.pow(38))]
    #[case::max_minus_1(format!("{}", i128::MAX - 1), i128::MAX - 1)]
    #[case::max(format!("{}", i128::MAX), i128::MAX)]
    #[case::min_plus_1(format!("{}", i128::MIN + 1), i128::MIN + 1)]
    #[case::min(format!("{}", i128::MIN), i128::MIN)]
    fn test_content_parse_i128_ok(#[case] input: impl AsRef<str>, #[case] expect: i128) {
        let mut an = FixedAnalyzer::new(input.as_ref().as_bytes().to_vec());
        assert_eq!(Token::Num, an.next());
        let content = an.content();
        assert_eq!(Ok(expect), content.parse_i128());
        assert_eq!(Ok(expect), lexical::Content::parse_i128(&content));
    }

    #[cfg(feature = "num_ext")]
    #[rstest]
    #[case::decimal_zero("10.0", NumError::Format)]
    #[case::decimal("3.14159", NumError::Format)]
    #[case::exponent_zero("0e0", NumError::Format)]
    #[case::exponent_positive("10E+1", NumError::Format)]
    #[case::exponent_negative("1E-98", NumError::Format)]
    #[case::range_i128_max_plus_1(format!("{}", i128::MAX as u128 + 1), NumError::Range)]
    #[case::range_u128_max(format!("{}", u128::MAX), NumError::Range)]
    fn test_content_parse_i128_err(#[case] input: impl AsRef<str>, #[case] expect: NumError) {
        let mut an = FixedAnalyzer::new(input.as_ref().as_bytes().to_vec());
        assert_eq!(Token::Num, an.next());
        let content = an.content();
        assert_eq!(Err(expect), content.parse_i128());
        assert_eq!(Err(expect), lexical::Content::parse_i128(&content));
    }

    #[cfg(feature = "num")]
    #[rstest]
    #[case::zero("0", 0.0)]
    #[case::minus_zero("-0", 0.0)]
    #[case::one("1", 1.0)]
    #[case::minus_one("-1", -1.0)]
    #[case::forty_two("42", 42.0)]
    #[case::pi("3.14159", 3.14159)]
    #[case::negative_decimal("-2.5", -2.5)]
    #[case::exponent_zero("0e0", 0.0)]
    #[case::exponent_positive("1e2", 100.0)]
    #[case::exponent_negative("1e-2", 0.01)]
    #[case::exponent_upper("1E2", 100.0)]
    #[case::exponent_plus_sign("1e+2", 100.0)]
    #[case::decimal_and_exponent("1.5e2", 150.0)]
    #[case::large_integer("9876543210", 9876543210.0)]
    #[case::f64_max(format!("{}", f64::MAX), f64::MAX)]
    #[case::f64_min_positive(format!("{}", f64::MIN_POSITIVE), f64::MIN_POSITIVE)]
    #[case::subnormal("5e-324", 5e-324)]
    fn test_content_parse_f64_ok(#[case] input: impl AsRef<str>, #[case] expect: f64) {
        let mut an = FixedAnalyzer::new(input.as_ref().as_bytes().to_vec());
        assert_eq!(Token::Num, an.next());
        let content = an.content();
        assert_eq!(Ok(expect), content.parse_f64());
        assert_eq!(Ok(expect), lexical::Content::parse_f64(&content));
    }

    #[cfg(feature = "num")]
    #[rstest]
    #[case::range_positive_overflow("1e309", NumError::Range)]
    #[case::range_negative_overflow("-1e309", NumError::Range)]
    fn test_content_parse_f64_err(#[case] input: impl AsRef<str>, #[case] expect: NumError) {
        let mut an = FixedAnalyzer::new(input.as_ref().as_bytes().to_vec());
        assert_eq!(Token::Num, an.next());
        let content = an.content();
        assert_eq!(Err(expect), content.parse_f64());
        assert_eq!(Err(expect), lexical::Content::parse_f64(&content));
    }

    #[rstest]
    #[case(
        ErrorKind::BadSurrogate { first: 0xd800, second: Some(0xd800), },
        "bad Unicode escape sequence surrogate pair: high surrogate '\\uD800' followed by invalid low surrogate '\\uD800' at line",
    )]
    #[case(
        ErrorKind::BadUtf8ContByte { seq_len: 3, offset: 2, value: 0x80 },
        "bad UTF-8 continuation byte 0x80 in 3-byte UTF-8 sequence (byte #2) at line",
    )]
    #[case(
        ErrorKind::UnexpectedByte { token: None, expect: Expect::TokenStartChar, actual: b'e' },
        "expected token start character but got character 'e' (ASCII 0x65) at line",
    )]
    #[case(
        ErrorKind::UnexpectedEof(Token::LitNull),
        "unexpected EOF in null token at line"
    )]
    fn test_error(#[case] kind: ErrorKind, #[case] expect: &str) {
        let pos = Pos::new(10, 2, 5);
        let err = Error { kind, pos };

        assert_eq!(kind, err.kind());
        assert_eq!(kind, lexical::Error::kind(&err));
        assert_eq!(pos, *err.pos());
        assert_eq!(pos, *lexical::Error::pos(&err));
        assert!(err.source().is_none());

        let actual = format!("{err}");
        assert!(
            actual.starts_with(expect),
            "expected {actual:?} to start with {expect:?}"
        );
    }

    #[test]
    fn test_analyzer_initial_state_content() {
        let an = FixedAnalyzer::new(vec![]);

        for _ in 0..5 {
            let content = an.content();
            assert_eq!("", content.literal());
            assert!(!content.is_escaped());
            assert_eq!("", content.unescaped());

            let content = an.try_content().unwrap();
            assert_eq!("", content.literal());
            assert!(!content.is_escaped());
            assert_eq!("", content.unescaped());
        }
    }

    #[test]
    #[should_panic(
        expected = "no error: last `next()` did not return `Token::Err` (use `content()` instead)"
    )]
    fn test_analyzer_initial_state_err() {
        let _ = FixedAnalyzer::new(vec![]).err();
    }

    #[rstest]
    #[case("", Token::Eof, None)]
    #[case("{", Token::ObjBegin, None)]
    #[case("}", Token::ObjEnd, None)]
    #[case("[", Token::ArrBegin, None)]
    #[case("]", Token::ArrEnd, None)]
    #[case(":", Token::NameSep, None)]
    #[case(",", Token::ValueSep, None)]
    #[case("false", Token::LitFalse, None)]
    #[case("null", Token::LitNull, None)]
    #[case("true", Token::LitTrue, None)]
    #[case("0", Token::Num, None)]
    #[case("-0", Token::Num, None)]
    #[case("1", Token::Num, None)]
    #[case("-1", Token::Num, None)]
    #[case("12", Token::Num, None)]
    #[case("-12", Token::Num, None)]
    #[case("0.0", Token::Num, None)]
    #[case("-0.0", Token::Num, None)]
    #[case("0.123456789", Token::Num, None)]
    #[case("-123.456789", Token::Num, None)]
    #[case("0E0", Token::Num, None)]
    #[case("0e0", Token::Num, None)]
    #[case("0E+0", Token::Num, None)]
    #[case("0e+0", Token::Num, None)]
    #[case("0E-0", Token::Num, None)]
    #[case("0e-0", Token::Num, None)]
    #[case("0.0E0", Token::Num, None)]
    #[case("0.0e0", Token::Num, None)]
    #[case("0.0E+0", Token::Num, None)]
    #[case("0.0e+0", Token::Num, None)]
    #[case("0.0E0", Token::Num, None)]
    #[case("0.0e0", Token::Num, None)]
    #[case("0E0", Token::Num, None)]
    #[case("0e0", Token::Num, None)]
    #[case("-0E+0", Token::Num, None)]
    #[case("-0e+0", Token::Num, None)]
    #[case("-0E-0", Token::Num, None)]
    #[case("-0e-0", Token::Num, None)]
    #[case("-0.0E0", Token::Num, None)]
    #[case("-0.0e0", Token::Num, None)]
    #[case("-0.0E+0", Token::Num, None)]
    #[case("-0.0e+0", Token::Num, None)]
    #[case("-0.0E0", Token::Num, None)]
    #[case("-0.0e0", Token::Num, None)]
    #[case("123E456", Token::Num, None)]
    #[case("123e456", Token::Num, None)]
    #[case("123.456E+7", Token::Num, None)]
    #[case("123.456e+7", Token::Num, None)]
    #[case("123.456E-89", Token::Num, None)]
    #[case("123.456e-89", Token::Num, None)]
    #[case("-123E456", Token::Num, None)]
    #[case("-123e456", Token::Num, None)]
    #[case("-123.456E+7", Token::Num, None)]
    #[case("-123.456e+7", Token::Num, None)]
    #[case("-123.456E-89", Token::Num, None)]
    #[case("-123.456e-89", Token::Num, None)]
    #[case(r#""""#, Token::Str, None)]
    #[case(r#"" ""#, Token::Str, None)]
    #[case(r#""foo""#, Token::Str, None)]
    #[case(r#""The quick brown fox jumped over the lazy dog!""#, Token::Str, None)]
    #[case(r#""\"""#, Token::Str, Some(r#"""""#))]
    #[case(r#""\\""#, Token::Str, Some(r#""\""#))]
    #[case(r#""\/""#, Token::Str, Some(r#""/""#))]
    #[case(r#""\t""#, Token::Str, Some("\"\t\""))]
    #[case(r#""\r""#, Token::Str, Some("\"\r\""))]
    #[case(r#""\n""#, Token::Str, Some("\"\n\""))]
    #[case(r#""\f""#, Token::Str, Some("\"\u{000c}\""))]
    #[case(r#""\b""#, Token::Str, Some("\"\u{0008}\""))]
    #[case(r#""\r\n""#, Token::Str, Some("\"\r\n\""))]
    #[case(r#""\u0000""#, Token::Str, Some("\"\u{0000}\""))]
    #[case(r#""\u001f""#, Token::Str, Some("\"\u{001f}\""))]
    #[case(r#""\u0020""#, Token::Str, Some(r#"" ""#))]
    #[case(r#""\u007E""#, Token::Str, Some(r#""~""#))]
    #[case(r#""\u007F""#, Token::Str, Some("\"\u{007f}\""))]
    #[case(r#""\u0080""#, Token::Str, Some("\"\u{0080}\""))]
    #[case(r#""\u0100""#, Token::Str, Some("\"\u{0100}\""))]
    #[case(r#""\ud7FF""#, Token::Str, Some("\"\u{d7ff}\""))]
    #[case(r#""\uE000""#, Token::Str, Some("\"\u{e000}\""))]
    #[case(r#""\ufDCf""#, Token::Str, Some("\"\u{fdcf}\""))]
    #[case(r#""\uFdeF""#, Token::Str, Some("\"\u{fdef}\""))]
    #[case(r#""\ufffd""#, Token::Str, Some("\"\u{fffd}\""))]
    #[case(r#""\uFFFE""#, Token::Str, Some("\"\u{fffe}\""))]
    #[case(r#""\uFFFF""#, Token::Str, Some("\"\u{ffff}\""))]
    #[case(r#""\ud800\udc00""#, Token::Str, Some("\"\u{10000}\""))] // Lowest valid surrogate pair → U+10000
    #[case(r#""\uD800\uDFFF""#, Token::Str, Some("\"\u{103ff}\""))] // High surrogate with highest low surrogate → U+103FF
    #[case(r#""\uDBFF\uDC00""#, Token::Str, Some("\"\u{10fc00}\""))] // Highest high surrogate with lowest low surrogate → U+10FC00
    #[case(r#""\udbFf\udfff""#, Token::Str, Some("\"\u{10ffff}\""))] // Highest valid surrogate pair → U+10FFFF (max Unicode scalar value)
    #[case(r#""\u0061b""#, Token::Str, Some(r#""ab""#))]
    #[case(r#""\uD800\uDC00a""#, Token::Str, Some("\"\u{10000}a\""))]
    #[case(r#""hello\nworld""#, Token::Str, Some("\"hello\nworld\""))]
    #[case(" ", Token::White, None)]
    #[case("\t", Token::White, None)]
    #[case("  ", Token::White, None)]
    #[case("\t\t", Token::White, None)]
    #[case(" \t \t    \t          \t\t", Token::White, None)]
    fn test_analyzer_single_token(
        #[case] input: &str,
        #[case] expect: Token,
        #[case] unescaped: Option<&str>,
    ) {
        // With content fetch.
        {
            let mut an = FixedAnalyzer::new(input.as_bytes());
            assert_eq!(Pos::default(), *an.pos());

            assert_eq!(expect, an.next());
            assert_eq!(Pos::default(), *an.pos());

            let content = an.content();
            assert_eq!(input, content.literal());
            assert_eq!(unescaped.is_some(), content.is_escaped());
            if let Some(u) = unescaped {
                assert_eq!(u, content.unescaped());
            } else {
                assert_eq!(input, content.unescaped());
            }

            assert_eq!(Token::Eof, an.next());
            assert_eq!(
                Pos {
                    offset: input.len(),
                    line: 1,
                    col: input.len() + 1
                },
                *an.pos()
            );

            assert_eq!(Token::Eof, an.next());
            assert_eq!(
                Pos {
                    offset: input.len(),
                    line: 1,
                    col: input.len() + 1
                },
                *an.pos()
            );
        }

        // Without content fetch.
        {
            let mut an = FixedAnalyzer::new(input.as_bytes());
            assert_eq!(Pos::default(), *an.pos());

            assert_eq!(expect, an.next());
            assert_eq!(Pos::default(), *an.pos());

            assert_eq!(Token::Eof, an.next());
            assert_eq!(
                Pos {
                    offset: input.len(),
                    line: 1,
                    col: input.len() + 1
                },
                *an.pos()
            );

            assert_eq!(Token::Eof, an.next());
            assert_eq!(
                Pos {
                    offset: input.len(),
                    line: 1,
                    col: input.len() + 1
                },
                *an.pos()
            );
        }
    }

    #[rstest]
    #[case("1".repeat(INLINE_LEN-1), Token::Num, None)]
    #[case("2".repeat(INLINE_LEN), Token::Num, None)]
    #[case("3".repeat(INLINE_LEN+1), Token::Num, None)]
    #[case(format!(r#""{}""#, "a".repeat(INLINE_LEN-3)), Token::Str, None)]
    #[case(format!(r#""{}""#, "b".repeat(INLINE_LEN-2)), Token::Str, None)]
    #[case(format!(r#""{}""#, "c".repeat(INLINE_LEN-1)), Token::Str, None)]
    #[case(format!(r#""{}""#, r#"\/"#.repeat(INLINE_LEN/2)), Token::Str, Some(format!(r#""{}""#, "/".repeat(INLINE_LEN/2))))]
    #[case(" ".repeat(INLINE_LEN-1), Token::White, None)]
    #[case("\t".repeat(INLINE_LEN), Token::White, None)]
    #[case(" ".repeat(INLINE_LEN+1), Token::White, None)]
    #[case(" \t".repeat(INLINE_LEN/2+1), Token::White, None)]
    fn test_analyzer_single_token_inline_len_boundary(
        #[case] input: String,
        #[case] expect: Token,
        #[case] unescaped: Option<String>,
    ) {
        let mut an = FixedAnalyzer::new(input.as_bytes());
        assert_eq!(Pos::default(), *an.pos());

        assert_eq!(expect, an.next());
        assert_eq!(Pos::default(), *an.pos());

        let content = an.content();
        assert_eq!(input, content.literal());
        assert_eq!(unescaped.is_some(), content.is_escaped());
        if let Some(u) = unescaped {
            assert_eq!(u, content.unescaped());
        } else {
            assert_eq!(input, content.unescaped());
        }

        assert_eq!(Token::Eof, an.next());
        assert_eq!(
            Pos {
                offset: input.len(),
                line: 1,
                col: input.len() + 1
            },
            *an.pos()
        );

        assert_eq!(Token::Eof, an.next());
        assert_eq!(
            Pos {
                offset: input.len(),
                line: 1,
                col: input.len() + 1
            },
            *an.pos()
        );
    }

    #[rstest]
    #[case(r#"["#)]
    #[case(r#"]"#)]
    #[case(r#"false"#)]
    #[case(r#":"#)]
    #[case(r#"null"#)]
    #[case(r#"3.14159e+0"#)]
    #[case(r#"{"#)]
    #[case(r#"}"#)]
    #[case(r#""foo\/\u1234\/bar""#)]
    #[case(r#"true"#)]
    #[case(r#","#)]
    #[case("\n\n\n   ")]
    #[should_panic(
        expected = "no error: last `next()` did not return `Token::Err` (use `content()` instead)"
    )]
    fn test_analyzer_single_token_panic_no_err(#[case] input: &str) {
        let mut an = FixedAnalyzer::new(input.as_bytes());

        let token = an.next();
        assert!(!token.is_terminal(), "input = {input:?}, token = {token:?}");

        let _ = an.err();
    }

    #[rstest]
    #[case("\"\u{0020}\"")]
    #[case("\"\u{007f}\"")] // DEL, the highest 1-byte UTF-8 character
    #[case("\"\u{0080}\"")] // Lowest two-byte UTF-8 character
    #[case("\"\u{07ff}\"")] // Highest two-byte UTF-8 character
    #[case("\"\u{0800}\"")] // Lowest three-byte UTF-8 character
    #[case("\"\u{d7ff}\"")] // Highest BMP code point before surrogates
    #[case("\"\u{e000}\"")] // Lowest BMP code point after surrogates
    #[case("\"\u{ffff}\"")] // Highest BMP code point: non-character but still valid JSON
    #[case("\"\u{10000}\"")] // Lowest four-byte UTF-8 character
    #[case("\"\u{10ffff}\"")] // Highest valid Unicode scalar value
    #[case("\"\u{3f086}\"")] // Regression test: 2026-02-14 🌹
    fn test_analyzer_utf8_char_1(#[case] input: &str) {
        // With content fetch.
        {
            let mut an = FixedAnalyzer::new(input.as_bytes());
            assert_eq!(Pos::default(), *an.pos());

            assert_eq!(Token::Str, an.next());
            assert_eq!(Pos::default(), *an.pos());

            let content = an.content();
            assert_eq!(input, content.literal());
            assert!(!content.is_escaped());
            assert_eq!(input, content.unescaped());

            assert_eq!(Token::Eof, an.next());
            assert_eq!(
                Pos {
                    offset: input.len(),
                    line: 1,
                    col: 4,
                },
                *an.pos()
            );

            assert_eq!(Token::Eof, an.next());
            assert_eq!(
                Pos {
                    offset: input.len(),
                    line: 1,
                    col: 4,
                },
                *an.pos()
            );
        }

        // Without content fetch.
        {
            let mut an = FixedAnalyzer::new(input.as_bytes());
            assert_eq!(Pos::default(), *an.pos());

            assert_eq!(Token::Str, an.next());
            assert_eq!(Pos::default(), *an.pos());

            assert_eq!(Token::Eof, an.next());
            assert_eq!(
                Pos {
                    offset: input.len(),
                    line: 1,
                    col: 4,
                },
                *an.pos()
            );

            assert_eq!(Token::Eof, an.next());
            assert_eq!(
                Pos {
                    offset: input.len(),
                    line: 1,
                    col: 4,
                },
                *an.pos()
            );
        }
    }

    #[rstest]
    // 2-byte followed by 1, 2, 3, 4-byte
    #[case::two_1("\"\u{00e4}a\"")]
    #[case::two_2("\"\u{00e4}\u{00e4}\"")]
    #[case::two_3("\"\u{00e4}\u{3042}\"")]
    #[case::two_4("\"\u{00e4}\u{10000}\"")]
    // 3-byte followed by 1, 2, 3, 4-byte
    #[case::three_1("\"\u{3042}a\"")]
    #[case::three_2("\"\u{3042}\u{00e4}\"")]
    #[case::three_3("\"\u{3042}\u{3042}\"")]
    #[case::three_4("\"\u{3042}\u{10000}\"")]
    // 4-byte followed by 1, 2, 3, 4-byte
    #[case::four_1("\"\u{10000}a\"")]
    #[case::four_2("\"\u{10000}\u{00e4}\"")]
    #[case::four_3("\"\u{10000}\u{3042}\"")]
    #[case::four_4("\"\u{10000}\u{10000}\"")]
    fn test_analyzer_utf8_char_2(#[case] input: &str) {
        // With content fetch.
        {
            let mut an = FixedAnalyzer::new(input.as_bytes());
            assert_eq!(Pos::default(), *an.pos());

            assert_eq!(Token::Str, an.next());
            assert_eq!(Pos::default(), *an.pos());

            let content = an.content();
            assert_eq!(input, content.literal());
            assert!(!content.is_escaped());
            assert_eq!(input, content.unescaped());

            assert_eq!(Token::Eof, an.next());
            assert_eq!(
                Pos {
                    offset: input.len(),
                    line: 1,
                    col: 5,
                },
                *an.pos()
            );

            assert_eq!(Token::Eof, an.next());
            assert_eq!(
                Pos {
                    offset: input.len(),
                    line: 1,
                    col: 5,
                },
                *an.pos()
            );
        }

        // Without content fetch.
        {
            let mut an = FixedAnalyzer::new(input.as_bytes());
            assert_eq!(Pos::default(), *an.pos());

            assert_eq!(Token::Str, an.next());
            assert_eq!(Pos::default(), *an.pos());

            assert_eq!(Token::Eof, an.next());
            assert_eq!(
                Pos {
                    offset: input.len(),
                    line: 1,
                    col: 5,
                },
                *an.pos()
            );

            assert_eq!(Token::Eof, an.next());
            assert_eq!(
                Pos {
                    offset: input.len(),
                    line: 1,
                    col: 5,
                },
                *an.pos()
            );
        }
    }

    #[rstest]
    #[case("\n", 2, 1)]
    #[case("\n\n", 3, 1)]
    #[case("\r", 2, 1)]
    #[case("\r\r", 3, 1)]
    #[case("\r\n", 2, 1)]
    #[case("\n\r", 3, 1)]
    #[case("\n\n\r\r", 5, 1)]
    #[case("\r\n\r", 3, 1)]
    #[case("\n\r\n", 3, 1)]
    #[case(" \n", 2, 1)]
    #[case("\n ", 2, 2)]
    #[case(" \r", 2, 1)]
    #[case("\r ", 2, 2)]
    #[case("\t\n", 2, 1)]
    #[case("\n ", 2, 2)]
    #[case("\t\r", 2, 1)]
    #[case("\r\t", 2, 2)]
    fn test_analyzer_whitespace_multiline(
        #[case] input: &str,
        #[case] line: usize,
        #[case] col: usize,
    ) {
        // With content fetch.
        {
            let mut an = FixedAnalyzer::new(input.as_bytes());
            assert_eq!(Pos::default(), *an.pos());

            assert_eq!(Token::White, an.next());
            assert_eq!(Pos::default(), *an.pos());

            let content = an.content();
            assert_eq!(input, content.literal());
            assert!(!content.is_escaped());
            assert_eq!(input, content.unescaped());

            assert_eq!(Token::Eof, an.next());
            assert_eq!(
                Pos {
                    offset: input.len(),
                    line,
                    col
                },
                *an.pos()
            );
        }

        // Without content fetch.
        {
            let mut an = FixedAnalyzer::new(input.as_bytes());
            assert_eq!(Pos::default(), *an.pos());

            assert_eq!(Token::White, an.next());
            assert_eq!(Pos::default(), *an.pos());

            assert_eq!(Token::Eof, an.next());
            assert_eq!(
                Pos {
                    offset: input.len(),
                    line,
                    col
                },
                *an.pos()
            );
        }
    }

    #[rstest]
    #[case("", T::t(Token::Eof, ""), T::t(Token::Eof, ""), 1, 1)]
    // =============================================================================================
    // Object start character `{` followed by something...
    // =============================================================================================
    #[case("{{", T::t(Token::ObjBegin, "{"), T::t(Token::ObjBegin, "{").pos(1, 1, 2), 1, 3)]
    #[case("{}", T::t(Token::ObjBegin, "{"), T::t(Token::ObjEnd, "}").pos(1, 1, 2), 1, 3)]
    #[case("{[", T::t(Token::ObjBegin, "{"), T::t(Token::ArrBegin, "[").pos(1, 1, 2), 1, 3)]
    #[case("{]", T::t(Token::ObjBegin, "{"), T::t(Token::ArrEnd, "]").pos(1, 1, 2), 1, 3)]
    #[case("{:", T::t(Token::ObjBegin, "{"), T::t(Token::NameSep, ":").pos(1, 1, 2), 1, 3)]
    #[case("{,", T::t(Token::ObjBegin, "{"), T::t(Token::ValueSep, ",").pos(1, 1, 2), 1, 3)]
    #[case("{false", T::t(Token::ObjBegin, "{"), T::t(Token::LitFalse, "false").pos(1, 1, 2), 1, 7)]
    #[case("{null", T::t(Token::ObjBegin, "{"), T::t(Token::LitNull, "null").pos(1, 1, 2), 1, 6)]
    #[case("{true", T::t(Token::ObjBegin, "{"), T::t(Token::LitTrue, "true").pos(1, 1, 2), 1, 6)]
    #[case("{0", T::t(Token::ObjBegin, "{"), T::t(Token::Num, "0").pos(1, 1, 2), 1, 3)]
    #[case("{-1.9", T::t(Token::ObjBegin, "{"), T::t(Token::Num, "-1.9").pos(1, 1, 2), 1, 6)]
    #[case("{3.14e+0", T::t(Token::ObjBegin, "{"), T::t(Token::Num, "3.14e+0").pos(1, 1, 2), 1, 9)]
    #[case(r#"{"""#, T::t(Token::ObjBegin, "{"), T::t(Token::Str, r#""""#).pos(1, 1, 2), 1, 4)]
    #[case(r#"{"foo""#, T::t(Token::ObjBegin, "{"), T::t(Token::Str, r#""foo""#).pos(1, 1, 2), 1, 7)]
    #[case(r#"{"hello\u0020there,\nworld!""#, T::t(Token::ObjBegin, "{"), T::t(Token::Str, r#""hello\u0020there,\nworld!""#).pos(1, 1, 2).unescaped("\"hello there,\nworld!\""), 1,29)]
    #[case("{ ", T::t(Token::ObjBegin, "{"), T::t(Token::White, " ").pos(1, 1, 2), 1, 3)]
    #[case("{\t\t\r\n\n ", T::t(Token::ObjBegin, "{"), T::t(Token::White, "\t\t\r\n\n ").pos(1, 1, 2), 3, 2)]
    // =============================================================================================
    // Object end character `}` followed by something...
    // =============================================================================================
    #[case("}{", T::t(Token::ObjEnd, "}"), T::t(Token::ObjBegin, "{").pos(1, 1, 2), 1, 3)]
    #[case("}}", T::t(Token::ObjEnd, "}"), T::t(Token::ObjEnd, "}").pos(1, 1, 2), 1, 3)]
    #[case("}[", T::t(Token::ObjEnd, "}"), T::t(Token::ArrBegin, "[").pos(1, 1, 2), 1, 3)]
    #[case("}]", T::t(Token::ObjEnd, "}"), T::t(Token::ArrEnd, "]").pos(1, 1, 2), 1, 3)]
    #[case("}:", T::t(Token::ObjEnd, "}"), T::t(Token::NameSep, ":").pos(1, 1, 2), 1, 3)]
    #[case("},", T::t(Token::ObjEnd, "}"), T::t(Token::ValueSep, ",").pos(1, 1, 2), 1, 3)]
    #[case("}false", T::t(Token::ObjEnd, "}"), T::t(Token::LitFalse, "false").pos(1, 1, 2), 1, 7)]
    #[case("}null", T::t(Token::ObjEnd, "}"), T::t(Token::LitNull, "null").pos(1, 1, 2), 1, 6)]
    #[case("}true", T::t(Token::ObjEnd, "}"), T::t(Token::LitTrue, "true").pos(1, 1, 2), 1, 6)]
    #[case("}0", T::t(Token::ObjEnd, "}"), T::t(Token::Num, "0").pos(1, 1, 2), 1, 3)]
    #[case("}-1.9", T::t(Token::ObjEnd, "}"), T::t(Token::Num, "-1.9").pos(1, 1, 2), 1, 6)]
    #[case("}3.14e+0", T::t(Token::ObjEnd, "}"), T::t(Token::Num, "3.14e+0").pos(1, 1, 2), 1, 9)]
    #[case(r#"}"""#, T::t(Token::ObjEnd, "}"), T::t(Token::Str, r#""""#).pos(1, 1, 2), 1, 4)]
    #[case(r#"}"foo""#, T::t(Token::ObjEnd, "}"), T::t(Token::Str, r#""foo""#).pos(1, 1, 2), 1, 7)]
    #[case(r#"}"hello\u0020there,\nworld!""#, T::t(Token::ObjEnd, "}"), T::t(Token::Str, r#""hello\u0020there,\nworld!""#).pos(1, 1, 2).unescaped("\"hello there,\nworld!\""), 1,29)]
    #[case("} ", T::t(Token::ObjEnd, "}"), T::t(Token::White, " ").pos(1, 1, 2), 1, 3)]
    #[case("}\t\t\r\n\n ", T::t(Token::ObjEnd, "}"), T::t(Token::White, "\t\t\r\n\n ").pos(1, 1, 2), 3, 2)]
    // =============================================================================================
    // Array start character `[` followed by something...
    // =============================================================================================
    #[case("[{", T::t(Token::ArrBegin, "["), T::t(Token::ObjBegin, "{").pos(1, 1, 2), 1, 3)]
    #[case("[}", T::t(Token::ArrBegin, "["), T::t(Token::ObjEnd, "}").pos(1, 1, 2), 1, 3)]
    #[case("[[", T::t(Token::ArrBegin, "["), T::t(Token::ArrBegin, "[").pos(1, 1, 2), 1, 3)]
    #[case("[]", T::t(Token::ArrBegin, "["), T::t(Token::ArrEnd, "]").pos(1, 1, 2), 1, 3)]
    #[case("[:", T::t(Token::ArrBegin, "["), T::t(Token::NameSep, ":").pos(1, 1, 2), 1, 3)]
    #[case("[,", T::t(Token::ArrBegin, "["), T::t(Token::ValueSep, ",").pos(1, 1, 2), 1, 3)]
    #[case("[false", T::t(Token::ArrBegin, "["), T::t(Token::LitFalse, "false").pos(1, 1, 2), 1, 7)]
    #[case("[null", T::t(Token::ArrBegin, "["), T::t(Token::LitNull, "null").pos(1, 1, 2), 1, 6)]
    #[case("[true", T::t(Token::ArrBegin, "["), T::t(Token::LitTrue, "true").pos(1, 1, 2), 1, 6)]
    #[case("[0", T::t(Token::ArrBegin, "["), T::t(Token::Num, "0").pos(1, 1, 2), 1, 3)]
    #[case("[-1.9", T::t(Token::ArrBegin, "["), T::t(Token::Num, "-1.9").pos(1, 1, 2), 1, 6)]
    #[case("[3.14e+0", T::t(Token::ArrBegin, "["), T::t(Token::Num, "3.14e+0").pos(1, 1, 2), 1, 9)]
    #[case(r#"["""#, T::t(Token::ArrBegin, "["), T::t(Token::Str, r#""""#).pos(1, 1, 2), 1, 4)]
    #[case(r#"["foo""#, T::t(Token::ArrBegin, "["), T::t(Token::Str, r#""foo""#).pos(1, 1, 2), 1, 7)]
    #[case(r#"["hello\u0020there,\nworld!""#, T::t(Token::ArrBegin, "["), T::t(Token::Str, r#""hello\u0020there,\nworld!""#).pos(1, 1, 2).unescaped("\"hello there,\nworld!\""), 1,29)]
    #[case("[ ", T::t(Token::ArrBegin, "["), T::t(Token::White, " ").pos(1, 1, 2), 1, 3)]
    #[case("[\t\t\r\n\n ", T::t(Token::ArrBegin, "["), T::t(Token::White, "\t\t\r\n\n ").pos(1, 1, 2), 3, 2)]
    // =============================================================================================
    // Array end character `]` followed by something...
    // =============================================================================================
    #[case("]{", T::t(Token::ArrEnd, "]"), T::t(Token::ObjBegin, "{").pos(1, 1, 2), 1, 3)]
    #[case("]}", T::t(Token::ArrEnd, "]"), T::t(Token::ObjEnd, "}").pos(1, 1, 2), 1, 3)]
    #[case("][", T::t(Token::ArrEnd, "]"), T::t(Token::ArrBegin, "[").pos(1, 1, 2), 1, 3)]
    #[case("]]", T::t(Token::ArrEnd, "]"), T::t(Token::ArrEnd, "]").pos(1, 1, 2), 1, 3)]
    #[case("]:", T::t(Token::ArrEnd, "]"), T::t(Token::NameSep, ":").pos(1, 1, 2), 1, 3)]
    #[case("],", T::t(Token::ArrEnd, "]"), T::t(Token::ValueSep, ",").pos(1, 1, 2), 1, 3)]
    #[case("]false", T::t(Token::ArrEnd, "]"), T::t(Token::LitFalse, "false").pos(1, 1, 2), 1, 7)]
    #[case("]null", T::t(Token::ArrEnd, "]"), T::t(Token::LitNull, "null").pos(1, 1, 2), 1, 6)]
    #[case("]true", T::t(Token::ArrEnd, "]"), T::t(Token::LitTrue, "true").pos(1, 1, 2), 1, 6)]
    #[case("]0", T::t(Token::ArrEnd, "]"), T::t(Token::Num, "0").pos(1, 1, 2), 1, 3)]
    #[case("]-1.9", T::t(Token::ArrEnd, "]"), T::t(Token::Num, "-1.9").pos(1, 1, 2), 1, 6)]
    #[case("]31.4e-1", T::t(Token::ArrEnd, "]"), T::t(Token::Num, "31.4e-1").pos(1, 1, 2), 1, 9)]
    #[case(r#"]"""#, T::t(Token::ArrEnd, "]"), T::t(Token::Str, r#""""#).pos(1, 1, 2), 1, 4)]
    #[case(r#"]"foo""#, T::t(Token::ArrEnd, "]"), T::t(Token::Str, r#""foo""#).pos(1, 1, 2), 1, 7)]
    #[case(r#"]"hello\u0020there,\nworld!""#, T::t(Token::ArrEnd, "]"), T::t(Token::Str, r#""hello\u0020there,\nworld!""#).pos(1, 1, 2).unescaped("\"hello there,\nworld!\""), 1,29)]
    #[case("] ", T::t(Token::ArrEnd, "]"), T::t(Token::White, " ").pos(1, 1, 2), 1, 3)]
    #[case("]\t\t\r\n\n ", T::t(Token::ArrEnd, "]"), T::t(Token::White, "\t\t\r\n\n ").pos(1, 1, 2), 3, 2)]
    // =============================================================================================
    // Name separator `:` followed by something...
    // =============================================================================================
    #[case(":{", T::t(Token::NameSep, ":"), T::t(Token::ObjBegin, "{").pos(1, 1, 2), 1, 3)]
    #[case(":}", T::t(Token::NameSep, ":"), T::t(Token::ObjEnd, "}").pos(1, 1, 2), 1, 3)]
    #[case(":[", T::t(Token::NameSep, ":"), T::t(Token::ArrBegin, "[").pos(1, 1, 2), 1, 3)]
    #[case(":]", T::t(Token::NameSep, ":"), T::t(Token::ArrEnd, "]").pos(1, 1, 2), 1, 3)]
    #[case("::", T::t(Token::NameSep, ":"), T::t(Token::NameSep, ":").pos(1, 1, 2), 1, 3)]
    #[case(":,", T::t(Token::NameSep, ":"), T::t(Token::ValueSep, ",").pos(1, 1, 2), 1, 3)]
    #[case(":false", T::t(Token::NameSep, ":"), T::t(Token::LitFalse, "false").pos(1, 1, 2), 1, 7)]
    #[case(":null", T::t(Token::NameSep, ":"), T::t(Token::LitNull, "null").pos(1, 1, 2), 1, 6)]
    #[case(":true", T::t(Token::NameSep, ":"), T::t(Token::LitTrue, "true").pos(1, 1, 2), 1, 6)]
    #[case(":0", T::t(Token::NameSep, ":"), T::t(Token::Num, "0").pos(1, 1, 2), 1, 3)]
    #[case(":-1.9", T::t(Token::NameSep, ":"), T::t(Token::Num, "-1.9").pos(1, 1, 2), 1, 6)]
    #[case(":31.4e-1", T::t(Token::NameSep, ":"), T::t(Token::Num, "31.4e-1").pos(1, 1, 2), 1, 9)]
    #[case(r#":"""#, T::t(Token::NameSep, ":"), T::t(Token::Str, r#""""#).pos(1, 1, 2), 1, 4)]
    #[case(r#":"foo""#, T::t(Token::NameSep, ":"), T::t(Token::Str, r#""foo""#).pos(1, 1, 2), 1, 7)]
    #[case(r#":"hello\u0020there,\nworld!""#, T::t(Token::NameSep, ":"), T::t(Token::Str, r#""hello\u0020there,\nworld!""#).pos(1, 1, 2).unescaped("\"hello there,\nworld!\""), 1,29)]
    #[case(": ", T::t(Token::NameSep, ":"), T::t(Token::White, " ").pos(1, 1, 2), 1, 3)]
    #[case(":\t\t\r\n\n ", T::t(Token::NameSep, ":"), T::t(Token::White, "\t\t\r\n\n ").pos(1, 1, 2), 3, 2)]
    // =============================================================================================
    // Value separator `,` followed by something...
    // =============================================================================================
    #[case(",{", T::t(Token::ValueSep, ","), T::t(Token::ObjBegin, "{").pos(1, 1, 2), 1, 3)]
    #[case(",}", T::t(Token::ValueSep, ","), T::t(Token::ObjEnd, "}").pos(1, 1, 2), 1, 3)]
    #[case(",[", T::t(Token::ValueSep, ","), T::t(Token::ArrBegin, "[").pos(1, 1, 2), 1, 3)]
    #[case(",]", T::t(Token::ValueSep, ","), T::t(Token::ArrEnd, "]").pos(1, 1, 2), 1, 3)]
    #[case(",:", T::t(Token::ValueSep, ","), T::t(Token::NameSep, ":").pos(1, 1, 2), 1, 3)]
    #[case(",,", T::t(Token::ValueSep, ","), T::t(Token::ValueSep, ",").pos(1, 1, 2), 1, 3)]
    #[case(",false", T::t(Token::ValueSep, ","), T::t(Token::LitFalse, "false").pos(1, 1, 2), 1, 7)]
    #[case(",null", T::t(Token::ValueSep, ","), T::t(Token::LitNull, "null").pos(1, 1, 2), 1, 6)]
    #[case(",true", T::t(Token::ValueSep, ","), T::t(Token::LitTrue, "true").pos(1, 1, 2), 1, 6)]
    #[case(",-0.0", T::t(Token::ValueSep, ","), T::t(Token::Num, "-0.0").pos(1, 1, 2), 1, 6)]
    #[case(",1.9", T::t(Token::ValueSep, ","), T::t(Token::Num, "1.9").pos(1, 1, 2), 1, 5)]
    #[case(",314e-2", T::t(Token::ValueSep, ","), T::t(Token::Num, "314e-2").pos(1, 1, 2), 1, 8)]
    #[case(r#","""#, T::t(Token::ValueSep, ","), T::t(Token::Str, r#""""#).pos(1, 1, 2), 1, 4)]
    #[case(r#","foo""#, T::t(Token::ValueSep, ","), T::t(Token::Str, r#""foo""#).pos(1, 1, 2), 1, 7)]
    #[case(r#","hello\u0020there,\nworld!""#, T::t(Token::ValueSep, ","), T::t(Token::Str, r#""hello\u0020there,\nworld!""#).pos(1, 1, 2).unescaped("\"hello there,\nworld!\""), 1,29)]
    #[case(", ", T::t(Token::ValueSep, ","), T::t(Token::White, " ").pos(1, 1, 2), 1, 3)]
    #[case(",\t\t\r\n\n ", T::t(Token::ValueSep, ","), T::t(Token::White, "\t\t\r\n\n ").pos(1, 1, 2), 3, 2)]
    // =============================================================================================
    // Literal `false` followed by something...
    // =============================================================================================
    #[case("false{", T::t(Token::LitFalse, "false"), T::t(Token::ObjBegin, "{").pos(5, 1, 6), 1, 7)]
    #[case("false}", T::t(Token::LitFalse, "false"), T::t(Token::ObjEnd, "}").pos(5, 1, 6), 1, 7)]
    #[case("false[", T::t(Token::LitFalse, "false"), T::t(Token::ArrBegin, "[").pos(5, 1, 6), 1, 7)]
    #[case("false]", T::t(Token::LitFalse, "false"), T::t(Token::ArrEnd, "]").pos(5, 1, 6), 1, 7)]
    #[case("false:", T::t(Token::LitFalse, "false"), T::t(Token::NameSep, ":").pos(5, 1, 6), 1, 7)]
    #[case("false,", T::t(Token::LitFalse, "false"), T::t(Token::ValueSep, ",").pos(5, 1, 6), 1, 7)]
    #[case("false ", T::t(Token::LitFalse, "false"), T::t(Token::White, " ").pos(5, 1, 6), 1, 7)]
    #[case("false\t", T::t(Token::LitFalse, "false"), T::t(Token::White, "\t").pos(5, 1, 6), 1, 7)]
    #[case("false\r", T::t(Token::LitFalse, "false"), T::t(Token::White, "\r").pos(5, 1, 6), 2, 1)]
    #[case("false\n", T::t(Token::LitFalse, "false"), T::t(Token::White, "\n").pos(5, 1, 6), 2, 1)]
    #[case("false\r\n", T::t(Token::LitFalse, "false"), T::t(Token::White, "\r\n").pos(5, 1, 6), 2, 1)]
    #[case("false\n\r", T::t(Token::LitFalse, "false"), T::t(Token::White, "\n\r").pos(5, 1, 6), 3, 1)]
    #[case("false\r\n ", T::t(Token::LitFalse, "false"), T::t(Token::White, "\r\n ").pos(5, 1, 6), 2, 2)]
    #[case("false\n\r ", T::t(Token::LitFalse, "false"), T::t(Token::White, "\n\r ").pos(5, 1, 6), 3, 2)]
    #[case("false \r\n", T::t(Token::LitFalse, "false"), T::t(Token::White, " \r\n").pos(5, 1, 6), 2, 1)]
    #[case("false \n\r", T::t(Token::LitFalse, "false"), T::t(Token::White, " \n\r").pos(5, 1, 6), 3, 1)]
    #[case(r#"false"""#, T::t(Token::LitFalse, "false"), T::t(Token::Str, r#""""#).pos(5, 1, 6), 1, 8)]
    // =============================================================================================
    // Literal `null` followed by something...
    // =============================================================================================
    #[case("null{", T::t(Token::LitNull, "null"), T::t(Token::ObjBegin, "{").pos(4, 1, 5), 1, 6)]
    #[case("null}", T::t(Token::LitNull, "null"), T::t(Token::ObjEnd, "}").pos(4, 1, 5), 1, 6)]
    #[case("null[", T::t(Token::LitNull, "null"), T::t(Token::ArrBegin, "[").pos(4, 1, 5), 1, 6)]
    #[case("null]", T::t(Token::LitNull, "null"), T::t(Token::ArrEnd, "]").pos(4, 1, 5), 1, 6)]
    #[case("null:", T::t(Token::LitNull, "null"), T::t(Token::NameSep, ":").pos(4, 1, 5), 1, 6)]
    #[case("null,", T::t(Token::LitNull, "null"), T::t(Token::ValueSep, ",").pos(4, 1, 5), 1, 6)]
    #[case("null ", T::t(Token::LitNull, "null"), T::t(Token::White, " ").pos(4, 1, 5), 1, 6)]
    #[case("null\t", T::t(Token::LitNull, "null"), T::t(Token::White, "\t").pos(4, 1, 5), 1, 6)]
    #[case("null\r", T::t(Token::LitNull, "null"), T::t(Token::White, "\r").pos(4, 1, 5), 2, 1)]
    #[case("null\n", T::t(Token::LitNull, "null"), T::t(Token::White, "\n").pos(4, 1, 5), 2, 1)]
    #[case(r#"null"x""#, T::t(Token::LitNull, "null"), T::t(Token::Str, r#""x""#).pos(4, 1, 5), 1, 8)]
    // =============================================================================================
    // Literal `true` followed by something...
    // =============================================================================================
    #[case("true{", T::t(Token::LitTrue, "true"), T::t(Token::ObjBegin, "{").pos(4, 1, 5), 1, 6)]
    #[case("true}", T::t(Token::LitTrue, "true"), T::t(Token::ObjEnd, "}").pos(4, 1, 5), 1, 6)]
    #[case("true[", T::t(Token::LitTrue, "true"), T::t(Token::ArrBegin, "[").pos(4, 1, 5), 1, 6)]
    #[case("true]", T::t(Token::LitTrue, "true"), T::t(Token::ArrEnd, "]").pos(4, 1, 5), 1, 6)]
    #[case("true:", T::t(Token::LitTrue, "true"), T::t(Token::NameSep, ":").pos(4, 1, 5), 1, 6)]
    #[case("true,", T::t(Token::LitTrue, "true"), T::t(Token::ValueSep, ",").pos(4, 1, 5), 1, 6)]
    #[case("true ", T::t(Token::LitTrue, "true"), T::t(Token::White, " ").pos(4, 1, 5), 1, 6)]
    #[case("true\t", T::t(Token::LitTrue, "true"), T::t(Token::White, "\t").pos(4, 1, 5), 1, 6)]
    #[case("true\r", T::t(Token::LitTrue, "true"), T::t(Token::White, "\r").pos(4, 1, 5), 2, 1)]
    #[case("true\n", T::t(Token::LitTrue, "true"), T::t(Token::White, "\n").pos(4, 1, 5), 2, 1)]
    #[case(r#"true"🧶""#, T::t(Token::LitTrue, "true"), T::t(Token::Str, r#""🧶""#).pos(4, 1, 5), 1, 8)]
    // =============================================================================================
    // Number followed by something...
    // =============================================================================================
    #[case("0{", T::t(Token::Num, "0"), T::t(Token::ObjBegin, "{").pos(1, 1, 2), 1, 3)]
    #[case("0}", T::t(Token::Num, "0"), T::t(Token::ObjEnd, "}").pos(1, 1, 2), 1, 3)]
    #[case("0[", T::t(Token::Num, "0"), T::t(Token::ArrBegin, "[").pos(1, 1, 2), 1, 3)]
    #[case("0]", T::t(Token::Num, "0"), T::t(Token::ArrEnd, "]").pos(1, 1, 2), 1, 3)]
    #[case("0:", T::t(Token::Num, "0"), T::t(Token::NameSep, ":").pos(1, 1, 2), 1, 3)]
    #[case("0,", T::t(Token::Num, "0"), T::t(Token::ValueSep, ",").pos(1, 1, 2), 1, 3)]
    #[case("0 ", T::t(Token::Num, "0"), T::t(Token::White, " ").pos(1, 1, 2), 1, 3)]
    #[case("0\t", T::t(Token::Num, "0"), T::t(Token::White, "\t").pos(1, 1, 2), 1, 3)]
    #[case("0\r", T::t(Token::Num, "0"), T::t(Token::White, "\r").pos(1, 1, 2), 2, 1)]
    #[case("0\n", T::t(Token::Num, "0"), T::t(Token::White, "\n").pos(1, 1, 2), 2, 1)]
    #[case(r#"0"""#, T::t(Token::Num, "0"), T::t(Token::Str, r#""""#).pos(1, 1, 2), 1, 4)]
    #[case("1{", T::t(Token::Num, "1"), T::t(Token::ObjBegin, "{").pos(1, 1, 2), 1, 3)]
    #[case("-9}", T::t(Token::Num, "-9"), T::t(Token::ObjEnd, "}").pos(2, 1, 3), 1, 4)]
    #[case("0.0[", T::t(Token::Num, "0.0"), T::t(Token::ArrBegin, "[").pos(3, 1, 4), 1, 5)]
    #[case("-0]", T::t(Token::Num, "-0"), T::t(Token::ArrEnd, "]").pos(2, 1, 3), 1, 4)]
    #[case(r#"-0"a""#, T::t(Token::Num, "-0"), T::t(Token::Str, r#""a""#).pos(2, 1, 3), 1, 6)]
    #[case("-0.0123456789:", T::t(Token::Num, "-0.0123456789"), T::t(Token::NameSep, ":").pos(13, 1, 14), 1, 15)]
    #[case("123456789e10,", T::t(Token::Num, "123456789e10"), T::t(Token::ValueSep, ",").pos(12, 1, 13), 1, 14)]
    #[case("0e-1 ", T::t(Token::Num, "0e-1"), T::t(Token::White, " ").pos(4, 1, 5), 1, 6)]
    #[case("2e+3\t", T::t(Token::Num, "2e+3"), T::t(Token::White, "\t").pos(4, 1, 5), 1, 6)]
    #[case("-5e6\r", T::t(Token::Num, "-5e6"), T::t(Token::White, "\r").pos(4, 1, 5), 2, 1)]
    #[case("6.7e89\n", T::t(Token::Num, "6.7e89"), T::t(Token::White, "\n").pos(6, 1, 7), 2, 1)]
    #[case(r#"1"a""#, T::t(Token::Num, "1"), T::t(Token::Str, r#""a""#).pos(1, 1, 2), 1, 5)]
    #[case(r#"2.5"a""#, T::t(Token::Num, "2.5"), T::t(Token::Str, r#""a""#).pos(3, 1, 4), 1, 7)]
    #[case(r#"3e4"a""#, T::t(Token::Num, "3e4"), T::t(Token::Str, r#""a""#).pos(3, 1, 4), 1, 7)]
    // =============================================================================================
    // String followed by something...
    // =============================================================================================
    #[case(r#"""{"#, T::t(Token::Str, r#""""#), T::t(Token::ObjBegin, "{").pos(2, 1, 3), 1, 4)]
    #[case(r#"""}"#, T::t(Token::Str, r#""""#), T::t(Token::ObjEnd, "}").pos(2, 1, 3), 1, 4)]
    #[case(r#"""["#, T::t(Token::Str, r#""""#), T::t(Token::ArrBegin, "[").pos(2, 1, 3), 1, 4)]
    #[case(r#"""]"#, T::t(Token::Str, r#""""#), T::t(Token::ArrEnd, "]").pos(2, 1, 3), 1, 4)]
    #[case(r#""":"#, T::t(Token::Str, r#""""#), T::t(Token::NameSep, ":").pos(2, 1, 3), 1, 4)]
    #[case(r#""","#, T::t(Token::Str, r#""""#), T::t(Token::ValueSep, ",").pos(2, 1, 3), 1, 4)]
    #[case(r#""" "#, T::t(Token::Str, r#""""#), T::t(Token::White, " ").pos(2, 1, 3), 1, 4)]
    #[case("\"\"\t", T::t(Token::Str, r#""""#), T::t(Token::White, "\t").pos(2, 1, 3), 1, 4)]
    #[case("\"\"\r", T::t(Token::Str, r#""""#), T::t(Token::White, "\r").pos(2, 1, 3), 2, 1)]
    #[case("\"\"\n", T::t(Token::Str, r#""""#), T::t(Token::White, "\n").pos(2, 1, 3), 2, 1)]
    #[case(r#""x"}"#, T::t(Token::Str, r#""x""#), T::t(Token::ObjEnd, "}").pos(3, 1, 4), 1, 5)]
    #[case(r#""foo bar"]"#, T::t(Token::Str, r#""foo bar""#), T::t(Token::ArrEnd, "]").pos(9, 1, 10), 1, 11)]
    #[case(r#""🧶":"#, T::t(Token::Str, r#""🧶""#), T::t(Token::NameSep, ":").pos(6, 1, 4), 1, 5)]
    #[case(r#""\"\t\r\n\\\/\u0020\"","#, T::t(Token::Str, r#""\"\t\r\n\\\/\u0020\"""#).unescaped("\"\"\t\r\n\\/ \"\""), T::t(Token::ValueSep, ",").pos(22, 1, 23), 1, 24)]
    #[case(r#""treble \uD834\uDD1E""clef""#, T::t(Token::Str, r#""treble \uD834\uDD1E""#).unescaped("\"treble 𝄞\""), T::t(Token::Str, r#""clef""#).pos(21, 1, 22), 1, 28)]
    #[case(r#""a"0e+12"#, T::t(Token::Str, r#""a""#), T::t(Token::Num, "0e+12").pos(3, 1, 4), 1, 9)]
    #[case(r#""❤😊"-0"#, T::t(Token::Str, r#""❤😊""#), T::t(Token::Num, "-0").pos(9, 1, 5), 1, 7)]
    #[case(r#""❤️😊"1"#, T::t(Token::Str, r#""❤️😊""#), T::t(Token::Num, "1").pos(12, 1, 6), 1, 7)]
    #[case(r#""""a""#, T::t(Token::Str, r#""""#), T::t(Token::Str, r#""a""#).pos(2, 1, 3), 1, 6)]
    #[case(r#""""café""#, T::t(Token::Str, r#""""#), T::t(Token::Str, r#""café""#).pos(2, 1, 3), 1, 9)]
    #[case(r#""a""""#, T::t(Token::Str, r#""a""#), T::t(Token::Str, r#""""#).pos(3, 1, 4), 1, 6)]
    #[case(r#""€10""""#, T::t(Token::Str, r#""€10""#), T::t(Token::Str, r#""""#).pos(7, 1, 6), 1, 8)]
    // =============================================================================================
    // Whitespace followed by something...
    // =============================================================================================
    #[case(" {", T::t(Token::White, " "), T::t(Token::ObjBegin, "{").pos(1, 1, 2), 1, 3)]
    #[case(" }", T::t(Token::White, " "), T::t(Token::ObjEnd, "}").pos(1, 1, 2), 1, 3)]
    #[case(" [", T::t(Token::White, " "), T::t(Token::ArrBegin, "[").pos(1, 1, 2), 1, 3)]
    #[case(" ]", T::t(Token::White, " "), T::t(Token::ArrEnd, "]").pos(1, 1, 2), 1, 3)]
    #[case(" :", T::t(Token::White, " "), T::t(Token::NameSep, ":").pos(1, 1, 2), 1, 3)]
    #[case(" ,", T::t(Token::White, " "), T::t(Token::ValueSep, ",").pos(1, 1, 2), 1, 3)]
    #[case(" false", T::t(Token::White, " "), T::t(Token::LitFalse, "false").pos(1, 1, 2), 1, 7)]
    #[case(" null", T::t(Token::White, " "), T::t(Token::LitNull, "null").pos(1, 1, 2), 1, 6)]
    #[case(" true", T::t(Token::White, " "), T::t(Token::LitTrue, "true").pos(1, 1, 2), 1, 6)]
    #[case(" 0", T::t(Token::White, " "), T::t(Token::Num, "0").pos(1, 1, 2), 1, 3)]
    #[case(" -0", T::t(Token::White, " "), T::t(Token::Num, "-0").pos(1, 1, 2), 1, 4)]
    #[case(r#" """#, T::t(Token::White, " "), T::t(Token::Str, r#""""#).pos(1, 1, 2), 1, 4)]
    #[case("\t{", T::t(Token::White, "\t"), T::t(Token::ObjBegin, "{").pos(1, 1, 2), 1, 3)]
    #[case("  {", T::t(Token::White, "  "), T::t(Token::ObjBegin, "{").pos(2, 1, 3), 1, 4)]
    #[case("\n{", T::t(Token::White, "\n"), T::t(Token::ObjBegin, "{").pos(1, 2, 1), 2, 2)]
    #[case("\r{", T::t(Token::White, "\r"), T::t(Token::ObjBegin, "{").pos(1, 2, 1), 2, 2)]
    #[case("\r\n{", T::t(Token::White, "\r\n"), T::t(Token::ObjBegin, "{").pos(2, 2, 1), 2, 2)]
    #[case("\n\r{", T::t(Token::White, "\n\r"), T::t(Token::ObjBegin, "{").pos(2, 3, 1), 3, 2)]
    #[case("\n\n{", T::t(Token::White, "\n\n"), T::t(Token::ObjBegin, "{").pos(2, 3, 1), 3, 2)]
    #[case("\r\r{", T::t(Token::White, "\r\r"), T::t(Token::ObjBegin, "{").pos(2, 3, 1), 3, 2)]
    fn test_analyzer_two_tokens(
        #[case] input: &str,
        #[case] t1: T,
        #[case] t2: T,
        #[case] line: usize,
        #[case] col: usize,
    ) {
        // With content fetch.
        {
            let mut an = FixedAnalyzer::new(input.as_bytes());
            assert_eq!(Pos::default(), *an.pos());

            assert_eq!(t1.token, an.next());
            assert_eq!(t1.pos, *an.pos());

            let content1 = an.content();
            assert_eq!(t1.literal, content1.literal());
            assert_eq!(t1.is_escaped(), content1.is_escaped());
            assert_eq!(t1.unescaped, content1.unescaped());

            assert_eq!(t2.token, an.next());
            assert_eq!(t2.pos, *an.pos());

            let content2 = an.content();
            assert_eq!(t2.literal, content2.literal());
            assert_eq!(t2.is_escaped(), content2.is_escaped());
            assert_eq!(t2.unescaped, content2.unescaped());

            assert_eq!(Token::Eof, an.next());
            assert_eq!(
                Pos {
                    offset: input.len(),
                    line,
                    col
                },
                *an.pos()
            );
        }

        // Without content fetch.
        {
            let mut an = FixedAnalyzer::new(input.as_bytes());
            assert_eq!(Pos::default(), *an.pos());

            assert_eq!(t1.token, an.next());
            assert_eq!(t1.pos, *an.pos());

            assert_eq!(t2.token, an.next());
            assert_eq!(t2.pos, *an.pos());

            assert_eq!(Token::Eof, an.next());
            assert_eq!(
                Pos {
                    offset: input.len(),
                    line,
                    col
                },
                *an.pos()
            );
        }
    }

    #[derive(Debug)]
    struct T {
        token: Token,
        pos: Pos,
        literal: &'static str,
        unescaped: &'static str,
    }

    impl T {
        fn t(token: Token, literal: &'static str) -> Self {
            Self {
                token,
                pos: Pos::default(),
                literal,
                unescaped: literal,
            }
        }

        fn pos(mut self, offset: usize, line: usize, col: usize) -> Self {
            self.pos = Pos { offset, line, col };
            self
        }

        fn unescaped(mut self, unescaped: &'static str) -> Self {
            self.unescaped = unescaped;
            self
        }

        fn is_escaped(&self) -> bool {
            self.unescaped != self.literal
        }
    }

    #[rstest]
    #[case(r#""\uDC00""#, 0xdc00, None, 3)]
    #[case(r#""\udc00""#, 0xdc00, None, 3)]
    #[case(r#""\uDFFF""#, 0xdfff, None, 3)]
    #[case(r#""\udfff""#, 0xdfff, None, 3)]
    #[case(r#""\uD800""#, 0xd800, None, 3)]
    #[case(r#""\ud800""#, 0xd800, None, 3)]
    #[case(r#""\uDBFF""#, 0xdbff, None, 3)]
    #[case(r#""\udbff""#, 0xdbff, None, 3)]
    #[case(r#""\uD800x""#, 0xd800, None, 3)]
    #[case(r#""\ud800x""#, 0xd800, None, 3)]
    #[case(r#""\uDBFFx""#, 0xdbff, None, 3)]
    #[case(r#""\udbffx""#, 0xdbff, None, 3)]
    #[case(r#""\uD800\""#, 0xd800, None, 3)]
    #[case(r#""\ud800\""#, 0xd800, None, 3)]
    #[case(r#""\uDBFF\""#, 0xdbff, None, 3)]
    #[case(r#""\udbff\""#, 0xdbff, None, 3)]
    #[case(r#""\uD800\/""#, 0xd800, None, 3)]
    #[case(r#""\ud800\t""#, 0xd800, None, 3)]
    #[case(r#""\uDBFF\r""#, 0xdbff, None, 3)]
    #[case(r#""\udbff\n""#, 0xdbff, None, 3)]
    #[case(r#""\uD800\ud800""#, 0xd800, Some(0xd800), 9)]
    #[case(r#""\uD800\uDBFF""#, 0xd800, Some(0xdbff), 9)]
    #[case(r#""\udbff\ue000""#, 0xdbff, Some(0xe000), 9)]
    #[case(r#""\udbff\u0000""#, 0xdbff, Some(0x0000), 9)]
    fn test_analyzer_single_error_bad_surrogate(
        #[case] input: &str,
        #[case] first: u16,
        #[case] second: Option<u16>,
        #[case] pos_offset: usize,
    ) {
        // With error fetch.
        {
            let mut an = FixedAnalyzer::new(input.as_bytes());
            assert_eq!(Pos::default(), *an.pos());

            assert_eq!(Token::Err, an.next());
            assert_eq!(Pos::default(), *an.pos());

            let err = an.err();
            assert_eq!(ErrorKind::BadSurrogate { first, second }, err.kind());
            assert_eq!(
                Pos {
                    offset: pos_offset,
                    line: 1,
                    col: pos_offset + 1
                },
                *err.pos()
            );

            assert_eq!(Token::Err, an.next());
            assert_eq!(Pos::default(), *an.pos());
        }

        // Without error fetch.
        {
            let mut an = FixedAnalyzer::new(input.as_bytes());
            assert_eq!(Pos::default(), *an.pos());

            assert_eq!(Token::Err, an.next());
            assert_eq!(Pos::default(), *an.pos());

            assert_eq!(Token::Err, an.next());
            assert_eq!(Pos::default(), *an.pos());
        }
    }

    #[rstest]
    #[case(&[0xc2, 0xc0], 1)]
    #[case(&[0xdf, 0xd0], 1)]
    #[case(&[0xe0, 0x7f, 0x80], 1)]
    #[case(&[0xe0, 0x80, 0x80], 1)]
    #[case(&[0xe0, 0xc0, 0x80], 1)]
    #[case(&[0xed, 0xa0, 0x80], 1)]
    #[case(&[0xed, 0xa0, 0xbf], 1)]
    #[case(&[0xed, 0xb0, 0x80], 1)]
    #[case(&[0xed, 0xb0, 0xbf], 1)]
    #[case(&[0xef, 0x7f, 0x80], 1)]
    #[case(&[0xef, 0xc0, 0x80], 1)]
    #[case(&[0xe0, 0x80, 0x7f], 2)]
    #[case(&[0xe0, 0x80, 0xc0], 2)]
    #[case(&[0xe0, 0xbf, 0x7f], 2)]
    #[case(&[0xe0, 0xbf, 0xc0], 2)]
    #[case(&[0xf0, 0x7f, 0x80, 0x80], 1)]
    #[case(&[0xf0, 0x80, 0x80, 0x80], 1)]
    #[case(&[0xf0, 0xc0, 0x80, 0x80], 1)]
    #[case(&[0xf4, 0x7f, 0x80, 0x80], 1)]
    #[case(&[0xf4, 0xc0, 0x80, 0x80], 1)]
    #[case(&[0xf4, 0x90, 0x80, 0x80], 1)]
    #[case(&[0xf0, 0x80, 0x7f, 0x80], 2)]
    #[case(&[0xf0, 0x80, 0xc0, 0x80], 2)]
    #[case(&[0xf0, 0xbf, 0x7f, 0x80], 2)]
    #[case(&[0xf0, 0xbf, 0xc0, 0x80], 2)]
    #[case(&[0xf0, 0x80, 0x80, 0x7f], 3)]
    #[case(&[0xf0, 0x80, 0x80, 0xc0], 3)]
    #[case(&[0xf0, 0xbf, 0xbf, 0x7f], 3)]
    #[case(&[0xf0, 0xbf, 0xbf, 0xc0], 3)]
    fn test_analyzer_single_error_bad_utf8_cont_byte(#[case] input: &[u8], #[case] offset: u8) {
        // Construct input buffer.
        let mut buf = Vec::with_capacity(input.len() + 1);
        buf.push(b'"');
        buf.extend_from_slice(input);

        // With error fetch.
        {
            let mut an = FixedAnalyzer::new(buf.clone());
            assert_eq!(Pos::default(), *an.pos());

            assert_eq!(Token::Err, an.next());
            assert_eq!(Pos::default(), *an.pos());

            let err = an.err();
            assert_eq!(
                ErrorKind::BadUtf8ContByte {
                    seq_len: input.len() as u8,
                    offset,
                    value: input[offset as usize]
                },
                err.kind()
            );
            assert_eq!(
                Pos {
                    offset: 1,
                    line: 1,
                    col: 2, // Second column is the entire multi-byte UTF-8 sequence.
                },
                *err.pos()
            );

            assert_eq!(Token::Err, an.next());
            assert_eq!(Pos::default(), *an.pos());
        }

        // Without error fetch.
        {
            let mut an = FixedAnalyzer::new(buf.clone());
            assert_eq!(Pos::default(), *an.pos());

            assert_eq!(Token::Err, an.next());
            assert_eq!(Pos::default(), *an.pos());

            assert_eq!(Token::Err, an.next());
            assert_eq!(Pos::default(), *an.pos());
        }
    }

    #[rstest]
    // =============================================================================================
    // Continuation bytes...
    // =============================================================================================
    #[case(0x80)]
    #[case(0x81)]
    #[case(0x82)]
    #[case(0x83)]
    #[case(0x84)]
    #[case(0x85)]
    #[case(0x86)]
    #[case(0x87)]
    #[case(0x88)]
    #[case(0x89)]
    #[case(0x8a)]
    #[case(0x8b)]
    #[case(0x8c)]
    #[case(0x8d)]
    #[case(0x8e)]
    #[case(0x8f)]
    #[case(0x90)]
    #[case(0x91)]
    #[case(0x92)]
    #[case(0x93)]
    #[case(0x94)]
    #[case(0x95)]
    #[case(0x96)]
    #[case(0x97)]
    #[case(0x98)]
    #[case(0x99)]
    #[case(0x9a)]
    #[case(0x9b)]
    #[case(0x9c)]
    #[case(0x9d)]
    #[case(0x9e)]
    #[case(0x9f)]
    #[case(0xa0)]
    #[case(0xa1)]
    #[case(0xa2)]
    #[case(0xa3)]
    #[case(0xa4)]
    #[case(0xa5)]
    #[case(0xa6)]
    #[case(0xa7)]
    #[case(0xa8)]
    #[case(0xa9)]
    #[case(0xaa)]
    #[case(0xab)]
    #[case(0xac)]
    #[case(0xad)]
    #[case(0xae)]
    #[case(0xaf)]
    #[case(0xb0)]
    #[case(0xb1)]
    #[case(0xb2)]
    #[case(0xb3)]
    #[case(0xb4)]
    #[case(0xb5)]
    #[case(0xb6)]
    #[case(0xb7)]
    #[case(0xb8)]
    #[case(0xb9)]
    #[case(0xba)]
    #[case(0xbb)]
    #[case(0xbc)]
    #[case(0xbd)]
    #[case(0xbe)]
    #[case(0xbf)]
    // =============================================================================================
    // Always produce overlong encodings...
    // =============================================================================================
    #[case(0xc0)]
    #[case(0xc1)]
    // =============================================================================================
    // Always produce code points beyond the Unicode range...
    // =============================================================================================
    #[case(0xf5)]
    #[case(0xf6)]
    #[case(0xf7)]
    #[case(0xf8)]
    #[case(0xf9)]
    #[case(0xfa)]
    #[case(0xfb)]
    #[case(0xfc)]
    #[case(0xfd)]
    #[case(0xfe)]
    #[case(0xff)]
    fn test_analyzer_single_error_bad_utf8_start_byte(#[case] b: u8) {
        // Construct input buffer.
        let mut buf = Vec::with_capacity(2);
        buf.push(b'"');
        buf.push(b);

        // With error fetch.
        {
            let mut an = FixedAnalyzer::new(buf.clone());
            assert_eq!(Pos::default(), *an.pos());

            assert_eq!(Token::Err, an.next());
            assert_eq!(Pos::default(), *an.pos());

            let err = an.err();
            assert_eq!(
                ErrorKind::UnexpectedByte {
                    token: Some(Token::Str),
                    expect: Expect::StrChar,
                    actual: b,
                },
                err.kind()
            );
            assert_eq!(
                Pos {
                    offset: 1,
                    line: 1,
                    col: 2,
                },
                *err.pos()
            );

            assert_eq!(Token::Err, an.next());
            assert_eq!(Pos::default(), *an.pos());
        }

        // Without error fetch.
        {
            let mut an = FixedAnalyzer::new(buf.clone());
            assert_eq!(Pos::default(), *an.pos());

            assert_eq!(Token::Err, an.next());
            assert_eq!(Pos::default(), *an.pos());

            assert_eq!(Token::Err, an.next());
            assert_eq!(Pos::default(), *an.pos());
        }
    }

    #[rstest]
    // =============================================================================================
    // Leading minus sign followed by bad character
    // =============================================================================================
    #[case("-}", Expect::Digit)]
    #[case("-]", Expect::Digit)]
    #[case("-a", Expect::Digit)]
    #[case("- ", Expect::Digit)]
    // =============================================================================================
    // Integer part starts with zero then followed by another digit
    // =============================================================================================
    #[case("00", Expect::DotExpOrBoundary)]
    #[case("01", Expect::DotExpOrBoundary)]
    #[case("02", Expect::DotExpOrBoundary)]
    #[case("03", Expect::DotExpOrBoundary)]
    #[case("04", Expect::DotExpOrBoundary)]
    #[case("05", Expect::DotExpOrBoundary)]
    #[case("06", Expect::DotExpOrBoundary)]
    #[case("07", Expect::DotExpOrBoundary)]
    #[case("08", Expect::DotExpOrBoundary)]
    #[case("09", Expect::DotExpOrBoundary)]
    #[case("-00", Expect::DotExpOrBoundary)]
    #[case("-01", Expect::DotExpOrBoundary)]
    #[case("-02", Expect::DotExpOrBoundary)]
    #[case("-03", Expect::DotExpOrBoundary)]
    #[case("-04", Expect::DotExpOrBoundary)]
    #[case("-05", Expect::DotExpOrBoundary)]
    #[case("-06", Expect::DotExpOrBoundary)]
    #[case("-07", Expect::DotExpOrBoundary)]
    #[case("-08", Expect::DotExpOrBoundary)]
    #[case("-09", Expect::DotExpOrBoundary)]
    // =============================================================================================
    // Integer part followed by a bad character
    // =============================================================================================
    #[case("0x", Expect::DotExpOrBoundary)]
    #[case("1x", Expect::DigitDotExpOrBoundary)]
    #[case("9/", Expect::DigitDotExpOrBoundary)]
    #[case("13456789000a", Expect::DigitDotExpOrBoundary)]
    // =============================================================================================
    // Integer part followed by a bad character
    // =============================================================================================
    #[case("0E,", Expect::DigitOrExpSign)]
    #[case("0e:", Expect::DigitOrExpSign)]
    #[case("1E ", Expect::DigitOrExpSign)]
    #[case("9ex", Expect::DigitOrExpSign)]
    // =============================================================================================
    // Dot followed by a bad character
    // =============================================================================================
    #[case("0.a", Expect::Digit)]
    #[case("0.{", Expect::Digit)]
    #[case("0.:", Expect::Digit)]
    #[case("0.-", Expect::Digit)]
    #[case("-0.a", Expect::Digit)]
    #[case("-0.{", Expect::Digit)]
    #[case("-0.:", Expect::Digit)]
    #[case("-0.-", Expect::Digit)]
    #[case("1.E", Expect::Digit)]
    #[case("2.e", Expect::Digit)]
    #[case("3.a", Expect::Digit)]
    #[case("4.a", Expect::Digit)]
    #[case("5.a", Expect::Digit)]
    #[case("6.a", Expect::Digit)]
    #[case("7.a", Expect::Digit)]
    #[case("8.a", Expect::Digit)]
    #[case("9.a", Expect::Digit)]
    #[case("-1.E", Expect::Digit)]
    #[case("-2.e", Expect::Digit)]
    #[case("-3.a", Expect::Digit)]
    #[case("-4.a", Expect::Digit)]
    #[case("-5.a", Expect::Digit)]
    #[case("-6.a", Expect::Digit)]
    #[case("-7.a", Expect::Digit)]
    #[case("-8.a", Expect::Digit)]
    #[case("-9.a", Expect::Digit)]
    #[case("10.E", Expect::Digit)]
    #[case("20.e", Expect::Digit)]
    #[case("30.a", Expect::Digit)]
    #[case("40.a", Expect::Digit)]
    #[case("50.a", Expect::Digit)]
    #[case("60.a", Expect::Digit)]
    #[case("70.a", Expect::Digit)]
    #[case("80.a", Expect::Digit)]
    #[case("90.a", Expect::Digit)]
    #[case("-10.E", Expect::Digit)]
    #[case("-20.e", Expect::Digit)]
    #[case("-30.a", Expect::Digit)]
    #[case("-40.a", Expect::Digit)]
    #[case("-50.a", Expect::Digit)]
    #[case("-60.a", Expect::Digit)]
    #[case("-70.a", Expect::Digit)]
    #[case("-80.a", Expect::Digit)]
    #[case("-90.a", Expect::Digit)]
    // =============================================================================================
    // Fractional part followed by bad character
    // =============================================================================================
    #[case("0.0|", Expect::DigitExpOrBoundary)]
    #[case("-0.0-", Expect::DigitExpOrBoundary)]
    #[case("1.0D", Expect::DigitExpOrBoundary)]
    #[case("-1.5d", Expect::DigitExpOrBoundary)]
    #[case("9.01F", Expect::DigitExpOrBoundary)]
    #[case("-9.001f", Expect::DigitExpOrBoundary)]
    #[case("100.001x", Expect::DigitExpOrBoundary)]
    // =============================================================================================
    // Exponent indicator 'E' or 'e' followed by a bad character
    // =============================================================================================
    #[case("0Ee", Expect::DigitOrExpSign)]
    #[case("-0e.", Expect::DigitOrExpSign)]
    #[case("1Ee", Expect::DigitOrExpSign)]
    #[case("-1e.", Expect::DigitOrExpSign)]
    #[case("2.0Ef", Expect::DigitOrExpSign)]
    #[case("-2.0ef", Expect::DigitOrExpSign)]
    #[case("3.01e.", Expect::DigitOrExpSign)]
    #[case("-456789.10111213141516171819E\"", Expect::DigitOrExpSign)]
    // =============================================================================================
    // Exponent sign '+' or '-' followed by a bad character
    // =============================================================================================
    #[case("0E++", Expect::Digit)]
    #[case("0e--", Expect::Digit)]
    #[case("1E+x", Expect::Digit)]
    #[case("2e+\"", Expect::Digit)]
    #[case("3E+:", Expect::Digit)]
    #[case("4e+,", Expect::Digit)]
    #[case("5E+{", Expect::Digit)]
    #[case("6e-}", Expect::Digit)]
    #[case("7E-[", Expect::Digit)]
    #[case("8e-]", Expect::Digit)]
    #[case("9E- ", Expect::Digit)]
    #[case("-0E+\t", Expect::Digit)]
    #[case("-0e-e", Expect::Digit)]
    #[case("-1E+E", Expect::Digit)]
    #[case("-2e+.", Expect::Digit)]
    #[case("-3E+!", Expect::Digit)]
    #[case("-4e+@", Expect::Digit)]
    #[case("-5E+#", Expect::Digit)]
    #[case("-6e-$", Expect::Digit)]
    #[case("-7E-%", Expect::Digit)]
    #[case("-8e-^", Expect::Digit)]
    #[case("-9E-&", Expect::Digit)]
    #[case("0.1E++", Expect::Digit)]
    #[case("0.1e--", Expect::Digit)]
    #[case("1.1E+x", Expect::Digit)]
    #[case("2.1e+\"", Expect::Digit)]
    #[case("3.1E+:", Expect::Digit)]
    #[case("4.1e+,", Expect::Digit)]
    #[case("5.1E+{", Expect::Digit)]
    #[case("6.1e-}", Expect::Digit)]
    #[case("7.1E-[", Expect::Digit)]
    #[case("8.1e-]", Expect::Digit)]
    #[case("9.1E- ", Expect::Digit)]
    #[case("-0.234E+\t", Expect::Digit)]
    #[case("-0.234e-e", Expect::Digit)]
    #[case("-1.234E+E", Expect::Digit)]
    #[case("-2.234e+.", Expect::Digit)]
    #[case("-3.234E+!", Expect::Digit)]
    #[case("-4.234e+@", Expect::Digit)]
    #[case("-5.234E+#", Expect::Digit)]
    #[case("-6.234e-$", Expect::Digit)]
    #[case("-7.234E-%", Expect::Digit)]
    #[case("-8.234e-^", Expect::Digit)]
    #[case("-9.234E-&", Expect::Digit)]
    // =============================================================================================
    // Exponent part followed by a bad character
    // =============================================================================================
    #[case("0E0e", Expect::DigitOrBoundary)]
    #[case("0E+0e", Expect::DigitOrBoundary)]
    #[case("0E-0e", Expect::DigitOrBoundary)]
    #[case("0.0e0e", Expect::DigitOrBoundary)]
    #[case("0.00e00e", Expect::DigitOrBoundary)]
    #[case("1.1E+1e", Expect::DigitOrBoundary)]
    #[case("11.11E+11e", Expect::DigitOrBoundary)]
    #[case("99.999E-999e", Expect::DigitOrBoundary)]
    fn test_analyzer_single_error_bad_number(#[case] input: &str, #[case] expect: Expect) {
        let mut an = FixedAnalyzer::new(input.as_bytes());

        assert_eq!(Token::Err, an.next());
        assert_eq!(Pos::default(), *an.pos());

        let err = an.err();
        assert_eq!(
            ErrorKind::UnexpectedByte {
                token: Some(Token::Num),
                expect,
                actual: *input.as_bytes().last().unwrap(),
            },
            err.kind(),
            "input={input:?}"
        );
        assert_eq!(
            Pos {
                offset: input.len() - 1,
                line: 1,
                col: input.len(),
            },
            *err.pos(),
            "input={input:?}"
        );

        assert_eq!(Token::Err, an.next(), "input={input:?}");
        assert_eq!(Pos::default(), *an.pos(), "input={input:?}");
    }

    #[rstest]
    #[case(r#"\0"#, Expect::EscChar)]
    #[case(r#"\a"#, Expect::EscChar)]
    #[case(r#"\v"#, Expect::EscChar)]
    #[case(r#"\x"#, Expect::EscChar)]
    #[case(r#"\uG"#, Expect::UnicodeEscHexDigit)]
    #[case(r#"\u:"#, Expect::UnicodeEscHexDigit)]
    #[case(r#"\u_"#, Expect::UnicodeEscHexDigit)]
    #[case(r#"\u0G"#, Expect::UnicodeEscHexDigit)]
    #[case(r#"\u1:"#, Expect::UnicodeEscHexDigit)]
    #[case(r#"\u2,"#, Expect::UnicodeEscHexDigit)]
    #[case(r#"\u3["#, Expect::UnicodeEscHexDigit)]
    #[case(r#"\u4]"#, Expect::UnicodeEscHexDigit)]
    #[case(r#"\u5{"#, Expect::UnicodeEscHexDigit)]
    #[case(r#"\u6}"#, Expect::UnicodeEscHexDigit)]
    #[case(r#"\u7."#, Expect::UnicodeEscHexDigit)]
    #[case(r#"\u8""#, Expect::UnicodeEscHexDigit)]
    #[case(r#"\u9g"#, Expect::UnicodeEscHexDigit)]
    #[case(r#"\uAG"#, Expect::UnicodeEscHexDigit)]
    #[case(r#"\ua_"#, Expect::UnicodeEscHexDigit)]
    #[case(r#"\uB_"#, Expect::UnicodeEscHexDigit)]
    #[case(r#"\ub_"#, Expect::UnicodeEscHexDigit)]
    #[case(r#"\uC_"#, Expect::UnicodeEscHexDigit)]
    #[case(r#"\uc_"#, Expect::UnicodeEscHexDigit)]
    #[case(r#"\uD_"#, Expect::UnicodeEscHexDigit)]
    #[case(r#"\ud_"#, Expect::UnicodeEscHexDigit)]
    #[case(r#"\uE_"#, Expect::UnicodeEscHexDigit)]
    #[case(r#"\ue_"#, Expect::UnicodeEscHexDigit)]
    #[case(r#"\uF_"#, Expect::UnicodeEscHexDigit)]
    #[case(r#"\uf_"#, Expect::UnicodeEscHexDigit)]
    #[case(r#"\u1a_"#, Expect::UnicodeEscHexDigit)]
    #[case(r#"\ub2C_"#, Expect::UnicodeEscHexDigit)]
    #[case(r#"\ud800\ug"#, Expect::UnicodeEscHexDigit)]
    #[case(r#"\ud800\u0:"#, Expect::UnicodeEscHexDigit)]
    #[case(r#"\ud800\u00:"#, Expect::UnicodeEscHexDigit)]
    #[case(r#"\ud800\u000:"#, Expect::UnicodeEscHexDigit)]
    fn test_analyzer_single_error_bad_escape(#[case] input: &str, #[case] expect: Expect) {
        let mut s = String::with_capacity(1 + input.len());
        s.push('"');
        s.push_str(input);

        let mut an = FixedAnalyzer::new(s.as_bytes());

        assert_eq!(Token::Err, an.next());
        assert_eq!(Pos::default(), *an.pos());

        let err = an.err();
        assert_eq!(
            ErrorKind::UnexpectedByte {
                token: Some(Token::Str),
                expect,
                actual: *input.as_bytes().last().unwrap(),
            },
            err.kind(),
            "input={input:?}"
        );
        assert_eq!(
            Pos {
                offset: s.len() - 1,
                line: 1,
                col: s.len(),
            },
            *err.pos(),
            "input={input:?}"
        );

        assert_eq!(Token::Err, an.next(), "input={input:?}");
        assert_eq!(Pos::default(), *an.pos(), "input={input:?}");
    }

    #[rstest]
    #[case::nul(0x00)]
    #[case::soh(0x01)]
    #[case::stx(0x02)]
    #[case::etx(0x03)]
    #[case::eot(0x04)]
    #[case::enq(0x05)]
    #[case::ack(0x06)]
    #[case::bel(0x07)]
    #[case::bs(0x08)]
    #[case::ht(0x09)]
    #[case::lf(0x0A)]
    #[case::vt(0x0B)]
    #[case::ff(0x0C)]
    #[case::cr(0x0D)]
    #[case::so(0x0E)]
    #[case::si(0x0F)]
    #[case::dle(0x10)]
    #[case::dc1(0x11)]
    #[case::dc2(0x12)]
    #[case::dc3(0x13)]
    #[case::dc4(0x14)]
    #[case::nak(0x15)]
    #[case::syn(0x16)]
    #[case::etb(0x17)]
    #[case::can(0x18)]
    #[case::em(0x19)]
    #[case::sub(0x1A)]
    #[case::esc(0x1B)]
    #[case::fs(0x1C)]
    #[case::gs(0x1D)]
    #[case::rs(0x1E)]
    #[case::us(0x1F)]
    fn test_analyzer_single_error_control_char(#[case] ctrl: u8) {
        static PREFIXES: [&str; 6] = ["", "a", r#"\u1234"#, "café", "𝄞", "🧶"];
        static COLS: [usize; 6] = [0, 1, 6, 4, 1, 1];
        let mut s: String = '"'.into();

        for (prefix, cols) in PREFIXES.iter().zip(COLS.iter().copied()) {
            s.truncate(1);
            s.push_str(prefix);
            s.push(ctrl as char);

            let mut an = FixedAnalyzer::new(s.as_bytes());

            assert_eq!(Token::Err, an.next());
            assert_eq!(Pos::default(), *an.pos());

            let err = an.err();
            assert_eq!(
                ErrorKind::UnexpectedByte {
                    token: Some(Token::Str),
                    expect: Expect::StrChar,
                    actual: ctrl,
                },
                err.kind(),
                "s={s:?}"
            );
            assert_eq!(
                Pos {
                    offset: s.len() - 1,
                    line: 1,
                    col: 2 + cols,
                },
                *err.pos(),
                "s={s:?}"
            );
        }
    }

    #[rstest]
    #[case("f", 'a', Token::LitFalse)]
    #[case("fa", 'l', Token::LitFalse)]
    #[case("fal", 's', Token::LitFalse)]
    #[case("fals", 'e', Token::LitFalse)]
    #[case("n", 'u', Token::LitNull)]
    #[case("nu", 'l', Token::LitNull)]
    #[case("nul", 'l', Token::LitNull)]
    #[case("t", 'r', Token::LitTrue)]
    #[case("tr", 'u', Token::LitTrue)]
    #[case("tru", 'e', Token::LitTrue)]
    fn test_analyzer_single_error_expect_char(
        #[case] input: &str,
        #[case] expect: char,
        #[case] expect_token: Token,
    ) {
        let bad_chars = &[
            b'[', b']', b':', b'{', b'}', b',', b'"', b'\\', b'$', b' ', b'\0', b'\t', b'A', b'x',
            b'X', b'0', b'9',
        ];
        let mut buf = Vec::with_capacity(input.len() + 1);
        buf.extend_from_slice(input.as_bytes());
        buf.push(b'_');

        for (i, actual) in bad_chars.into_iter().enumerate() {
            buf[input.len()] = *actual;

            let mut an = FixedAnalyzer::new(buf.clone());

            assert_eq!(Token::Err, an.next());
            assert_eq!(Pos::default(), *an.pos());

            let err = an.err();
            assert_eq!(
                ErrorKind::UnexpectedByte {
                    token: Some(expect_token),
                    expect: Expect::Char(expect),
                    actual: *actual,
                },
                err.kind(),
                "input={input:?}, i={i}, actual={actual:02x}"
            );
            assert_eq!(
                Pos {
                    offset: input.len(),
                    line: 1,
                    col: buf.len(),
                },
                *err.pos(),
                "input={input:?}, i={i}, actual={actual:02x}"
            );

            assert_eq!(
                Token::Err,
                an.next(),
                "input={input:?}, i={i}, actual={actual:02x}"
            );
            assert_eq!(
                Pos::default(),
                *an.pos(),
                "input={input:?}, i={i}, actual={actual:02x}"
            );
        }
    }

    #[rstest]
    #[case("falsep", Token::LitFalse)]
    #[case("nullE", Token::LitNull)]
    #[case("true0", Token::LitTrue)]
    fn test_analyzer_single_error_expect_boundary(
        #[case] input: &str,
        #[case] expect_token: Token,
    ) {
        let actual = input.as_bytes().last().copied().unwrap();
        let mut an = FixedAnalyzer::new(input.as_bytes());

        assert_eq!(Token::Err, an.next());
        assert_eq!(Pos::default(), *an.pos());

        let err = an.err();
        assert_eq!(
            ErrorKind::UnexpectedByte {
                token: Some(expect_token),
                expect: Expect::Boundary,
                actual,
            },
            err.kind(),
        );
        assert_eq!(
            Pos {
                offset: input.len() - 1,
                line: 1,
                col: input.len(),
            },
            *err.pos(),
        );
    }

    #[rstest]
    #[case(r#"f"#, Token::LitFalse)]
    #[case(r#"fa"#, Token::LitFalse)]
    #[case(r#"fal"#, Token::LitFalse)]
    #[case(r#"n"#, Token::LitNull)]
    #[case(r#"nu"#, Token::LitNull)]
    #[case(r#"nul"#, Token::LitNull)]
    #[case(r#"-"#, Token::Num)]
    #[case(r#"0."#, Token::Num)]
    #[case(r#"1."#, Token::Num)]
    #[case(r#"2."#, Token::Num)]
    #[case(r#"3."#, Token::Num)]
    #[case(r#"4."#, Token::Num)]
    #[case(r#"5."#, Token::Num)]
    #[case(r#"6."#, Token::Num)]
    #[case(r#"7."#, Token::Num)]
    #[case(r#"8."#, Token::Num)]
    #[case(r#"9."#, Token::Num)]
    #[case(r#"10."#, Token::Num)]
    #[case(r#"0E"#, Token::Num)]
    #[case(r#"0E+"#, Token::Num)]
    #[case(r#"0E-"#, Token::Num)]
    #[case(r#"0e"#, Token::Num)]
    #[case(r#"0e+"#, Token::Num)]
    #[case(r#"0e-"#, Token::Num)]
    #[case(r#"1.0E"#, Token::Num)]
    #[case(r#"1.0E+"#, Token::Num)]
    #[case(r#"1.0E-"#, Token::Num)]
    #[case(r#"1.0e"#, Token::Num)]
    #[case(r#"1.0e+"#, Token::Num)]
    #[case(r#"1.0e-"#, Token::Num)]
    #[case(r#"""#, Token::Str)]
    #[case(r#""a"#, Token::Str)]
    #[case(r#""\"#, Token::Str)]
    #[case(r#""\u"#, Token::Str)]
    #[case(r#""\u1"#, Token::Str)]
    #[case(r#""\u12"#, Token::Str)]
    #[case(r#""\u123"#, Token::Str)]
    #[case(r#""\u1234"#, Token::Str)]
    #[case(r#""\u1234 foo bar"#, Token::Str)]
    #[case(r#"t"#, Token::LitTrue)]
    #[case(r#"tr"#, Token::LitTrue)]
    #[case(r#"tru"#, Token::LitTrue)]
    fn test_analyzer_single_error_unexpected_eof(#[case] input: &str, #[case] expect: Token) {
        // With error fetch.
        {
            let mut an = FixedAnalyzer::new(input.as_bytes());
            assert_eq!(Pos::default(), *an.pos());

            assert_eq!(Token::Err, an.next());
            assert_eq!(Pos::default(), *an.pos());

            let err = an.err();
            assert_eq!(
                ErrorKind::UnexpectedEof(expect),
                err.kind(),
                "input = {input:?}, expect = {expect:?}"
            );
            assert_eq!(
                Pos {
                    offset: input.len(),
                    line: 1,
                    col: 1 + input.len(),
                },
                *err.pos(),
                "input = {input:?}, expect = {expect:?}"
            );

            assert_eq!(Token::Err, an.next());
            assert_eq!(Pos::default(), *an.pos());
        }

        // Without error fetch.
        {
            let mut an = FixedAnalyzer::new(input.as_bytes());
            assert_eq!(Pos::default(), *an.pos());

            assert_eq!(Token::Err, an.next());
            assert_eq!(Pos::default(), *an.pos());

            assert_eq!(Token::Err, an.next());
            assert_eq!(Pos::default(), *an.pos());
        }
    }

    #[rstest]
    #[case(0x00)]
    #[case(0x01)]
    #[case(0x02)]
    #[case(0x03)]
    #[case(0x04)]
    #[case(0x05)]
    #[case(0x06)]
    #[case(0x07)]
    #[case(0x08)]
    #[case(0x0b)]
    #[case(0x0c)]
    #[case(0x0e)]
    #[case(0x0f)]
    #[case(0x10)]
    #[case(0x11)]
    #[case(0x12)]
    #[case(0x13)]
    #[case(0x14)]
    #[case(0x15)]
    #[case(0x16)]
    #[case(0x17)]
    #[case(0x18)]
    #[case(0x19)]
    #[case(0x1a)]
    #[case(0x1b)]
    #[case(0x1c)]
    #[case(0x1d)]
    #[case(0x1e)]
    #[case(0x1f)]
    #[case(b'\'')]
    #[case(b'+')]
    #[case(b'.')]
    #[case(b'E')]
    #[case(b'\\')]
    #[case(b'e')]
    #[case(0x7f)]
    #[case(0x80)]
    #[case(0xbf)]
    #[case(0xc0)]
    #[case(0xc7)]
    #[case(0xcf)]
    #[case(0xd0)]
    #[case(0xd7)]
    #[case(0xdf)]
    #[case(0xe0)]
    #[case(0xe7)]
    #[case(0xef)]
    #[case(0xf0)]
    #[case(0xf7)]
    #[case(0xff)]
    fn test_analyzer_error_non_token_start(#[case] bad: u8) {
        // Bad character occurs at the very start of the text.
        {
            let mut an = FixedAnalyzer::new(vec![bad]);
            assert_eq!(Pos::default(), *an.pos());

            assert_eq!(Token::Err, an.next());
            assert_eq!(Pos::default(), *an.pos());

            let err = an.err();
            assert_eq!(
                ErrorKind::UnexpectedByte {
                    token: None,
                    expect: Expect::TokenStartChar,
                    actual: bad
                },
                err.kind(),
                "bad = {bad:02x}"
            );
            assert_eq!(Pos::default(), *err.pos(), "bad = {bad:02x}");

            assert_eq!(Token::Err, an.next());
            assert_eq!(Pos::default(), *an.pos());
        }

        // Bad character occurs after a valid token.
        {
            let valid_list = [
                "[",
                "]",
                "false ",
                "null ",
                "1 ",
                "{",
                "}",
                r#""a""#,
                r#""\u0000 foo \\//""#,
                "true\t",
            ];

            for (i, valid) in valid_list.into_iter().enumerate() {
                let mut buf: Vec<u8> = Vec::with_capacity(valid.len() + 1);
                buf.extend_from_slice(valid.as_bytes());
                buf.push(bad);

                let mut an = FixedAnalyzer::new(buf);

                let token = an.next();
                assert!(
                    !token.is_terminal(),
                    "valid = {valid:?}, i = {i}, bad = {bad:02x}"
                );
                if token.is_literal() || token == Token::Num {
                    assert_eq!(
                        Token::White,
                        an.next(),
                        "valid = {valid:?}, i = {i}, bad = {bad:02x}"
                    );
                }

                assert_eq!(Token::Err, an.next());
                let err = an.err();
                assert_eq!(
                    ErrorKind::UnexpectedByte {
                        token: None,
                        expect: Expect::TokenStartChar,
                        actual: bad
                    },
                    err.kind(),
                    "valid = {valid:?}, i = {i}, bad = {bad:02x}"
                );
                assert_eq!(
                    Pos {
                        offset: valid.len(),
                        line: 1,
                        col: 1 + valid.len(),
                    },
                    *err.pos(),
                    "valid = {valid:?}, i = {i}, bad = {bad:02x}"
                );
            }
        }
    }

    #[rstest]
    #[case(br#"123.456789:a"#)]
    #[case(br#"<"#)]
    #[case(br#""foo" "bar" "baz"#)]
    #[should_panic(
        expected = "no content: last `next()` returned `Token::Err` (use `err()` instead)"
    )]
    fn test_analyzer_panic_no_content(#[case] input: &[u8]) {
        let mut an = FixedAnalyzer::new(input);

        loop {
            if an.next() == Token::Err {
                break;
            }
        }

        let _ = an.content();
    }

    #[test]
    fn test_analyzer_smoke() {
        const JSON_TEXT: &str = r#"{
  "foo":["bar",1,5e-7, false, null  ,true, {"baz":"\\\"aââbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb©¢çc\"\\","qux":[{},{},null]}],
  "Lorem ipsum dolor sit amet, consectetur adipiscing elit." : "Cras sed ipsum at arcu porta blandit. Nunc eu mauris lacus. Vivamus dignissim tincidunt gravida. Fusce quis neque enim. Sed ac leo neque. Praesent feugiat efficitur eros, quis venenatis urna porttitor condimentum. Mauris finibus dui non vulputate mattis. Nullam scelerisque nibh vel dui egestas luctus. Vestibulum commodo mi ex. In laoreet hendrerit fringilla.\n\nPraesent vel ex sed dolor fermentum lobortis.",
  "👋":   ["🌎","🌏", "🌏", "こんにちは、世界"],
  "abc\u0020123": {{{"inner":[[[-1,-2.0,-3.00e+0,-4E-0,3.141592653589793238462643383279,null]]]}}}
}"#;
        const EXPECT: &[(Token, Pos, &str, Option<&str>)] = &[
            // Line 1
            (
                Token::ObjBegin,
                Pos {
                    offset: 0,
                    line: 1,
                    col: 1,
                },
                "{",
                None,
            ),
            (
                Token::White,
                Pos {
                    offset: 1,
                    line: 1,
                    col: 2,
                },
                "\n  ",
                None,
            ),
            // Line 2
            (
                Token::Str,
                Pos {
                    offset: 4,
                    line: 2,
                    col: 3,
                },
                r#""foo""#,
                None,
            ),
            (
                Token::NameSep,
                Pos {
                    offset: 9,
                    line: 2,
                    col: 8,
                },
                ":",
                None,
            ),
            (
                Token::ArrBegin,
                Pos {
                    offset: 10,
                    line: 2,
                    col: 9,
                },
                "[",
                None,
            ),
            (
                Token::Str,
                Pos {
                    offset: 11,
                    line: 2,
                    col: 10,
                },
                r#""bar""#,
                None,
            ),
            (
                Token::ValueSep,
                Pos {
                    offset: 16,
                    line: 2,
                    col: 15,
                },
                ",",
                None,
            ),
            (
                Token::Num,
                Pos {
                    offset: 17,
                    line: 2,
                    col: 16,
                },
                "1",
                None,
            ),
            (
                Token::ValueSep,
                Pos {
                    offset: 18,
                    line: 2,
                    col: 17,
                },
                ",",
                None,
            ),
            (
                Token::Num,
                Pos {
                    offset: 19,
                    line: 2,
                    col: 18,
                },
                "5e-7",
                None,
            ),
            (
                Token::ValueSep,
                Pos {
                    offset: 23,
                    line: 2,
                    col: 22,
                },
                ",",
                None,
            ),
            (
                Token::White,
                Pos {
                    offset: 24,
                    line: 2,
                    col: 23,
                },
                " ",
                None,
            ),
            (
                Token::LitFalse,
                Pos {
                    offset: 25,
                    line: 2,
                    col: 24,
                },
                "false",
                None,
            ),
            (
                Token::ValueSep,
                Pos {
                    offset: 30,
                    line: 2,
                    col: 29,
                },
                ",",
                None,
            ),
            (
                Token::White,
                Pos {
                    offset: 31,
                    line: 2,
                    col: 30,
                },
                " ",
                None,
            ),
            (
                Token::LitNull,
                Pos {
                    offset: 32,
                    line: 2,
                    col: 31,
                },
                "null",
                None,
            ),
            (
                Token::White,
                Pos {
                    offset: 36,
                    line: 2,
                    col: 35,
                },
                "  ",
                None,
            ),
            (
                Token::ValueSep,
                Pos {
                    offset: 38,
                    line: 2,
                    col: 37,
                },
                ",",
                None,
            ),
            (
                Token::LitTrue,
                Pos {
                    offset: 39,
                    line: 2,
                    col: 38,
                },
                "true",
                None,
            ),
            (
                Token::ValueSep,
                Pos {
                    offset: 43,
                    line: 2,
                    col: 42,
                },
                ",",
                None,
            ),
            (
                Token::White,
                Pos {
                    offset: 44,
                    line: 2,
                    col: 43,
                },
                " ",
                None,
            ),
            (
                Token::ObjBegin,
                Pos {
                    offset: 45,
                    line: 2,
                    col: 44,
                },
                "{",
                None,
            ),
            (
                Token::Str,
                Pos {
                    offset: 46,
                    line: 2,
                    col: 45,
                },
                r#""baz""#,
                None,
            ),
            (
                Token::NameSep,
                Pos {
                    offset: 51,
                    line: 2,
                    col: 50,
                },
                ":",
                None,
            ),
            (
                Token::Str,
                Pos {
                    offset: 52,
                    line: 2,
                    col: 51,
                },
                r#""\\\"aââbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb©¢çc\"\\""#,
                Some(
                    r#""\"aââbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb©¢çc"\""#,
                ),
            ),
            (
                Token::ValueSep,
                Pos {
                    offset: 149,
                    line: 2,
                    col: 143,
                },
                ",",
                None,
            ),
            (
                Token::Str,
                Pos {
                    offset: 150,
                    line: 2,
                    col: 144,
                },
                r#""qux""#,
                None,
            ),
            (
                Token::NameSep,
                Pos {
                    offset: 155,
                    line: 2,
                    col: 149,
                },
                ":",
                None,
            ),
            (
                Token::ArrBegin,
                Pos {
                    offset: 156,
                    line: 2,
                    col: 150,
                },
                "[",
                None,
            ),
            (
                Token::ObjBegin,
                Pos {
                    offset: 157,
                    line: 2,
                    col: 151,
                },
                "{",
                None,
            ),
            (
                Token::ObjEnd,
                Pos {
                    offset: 158,
                    line: 2,
                    col: 152,
                },
                "}",
                None,
            ),
            (
                Token::ValueSep,
                Pos {
                    offset: 159,
                    line: 2,
                    col: 153,
                },
                ",",
                None,
            ),
            (
                Token::ObjBegin,
                Pos {
                    offset: 160,
                    line: 2,
                    col: 154,
                },
                "{",
                None,
            ),
            (
                Token::ObjEnd,
                Pos {
                    offset: 161,
                    line: 2,
                    col: 155,
                },
                "}",
                None,
            ),
            (
                Token::ValueSep,
                Pos {
                    offset: 162,
                    line: 2,
                    col: 156,
                },
                ",",
                None,
            ),
            (
                Token::LitNull,
                Pos {
                    offset: 163,
                    line: 2,
                    col: 157,
                },
                "null",
                None,
            ),
            (
                Token::ArrEnd,
                Pos {
                    offset: 167,
                    line: 2,
                    col: 161,
                },
                "]",
                None,
            ),
            (
                Token::ObjEnd,
                Pos {
                    offset: 168,
                    line: 2,
                    col: 162,
                },
                "}",
                None,
            ),
            (
                Token::ArrEnd,
                Pos {
                    offset: 169,
                    line: 2,
                    col: 163,
                },
                "]",
                None,
            ),
            (
                Token::ValueSep,
                Pos {
                    offset: 170,
                    line: 2,
                    col: 164,
                },
                ",",
                None,
            ),
            (
                Token::White,
                Pos {
                    offset: 171,
                    line: 2,
                    col: 165,
                },
                "\n  ",
                None,
            ),
            // Line 3
            (
                Token::Str,
                Pos {
                    offset: 174,
                    line: 3,
                    col: 3,
                },
                r#""Lorem ipsum dolor sit amet, consectetur adipiscing elit.""#,
                None,
            ),
            (
                Token::White,
                Pos {
                    offset: 232,
                    line: 3,
                    col: 61,
                },
                " ",
                None,
            ),
            (
                Token::NameSep,
                Pos {
                    offset: 233,
                    line: 3,
                    col: 62,
                },
                ":",
                None,
            ),
            (
                Token::White,
                Pos {
                    offset: 234,
                    line: 3,
                    col: 63,
                },
                " ",
                None,
            ),
            (
                Token::Str,
                Pos {
                    offset: 235,
                    line: 3,
                    col: 64,
                },
                r#""Cras sed ipsum at arcu porta blandit. Nunc eu mauris lacus. Vivamus dignissim tincidunt gravida. Fusce quis neque enim. Sed ac leo neque. Praesent feugiat efficitur eros, quis venenatis urna porttitor condimentum. Mauris finibus dui non vulputate mattis. Nullam scelerisque nibh vel dui egestas luctus. Vestibulum commodo mi ex. In laoreet hendrerit fringilla.\n\nPraesent vel ex sed dolor fermentum lobortis.""#,
                Some(
                    r#""Cras sed ipsum at arcu porta blandit. Nunc eu mauris lacus. Vivamus dignissim tincidunt gravida. Fusce quis neque enim. Sed ac leo neque. Praesent feugiat efficitur eros, quis venenatis urna porttitor condimentum. Mauris finibus dui non vulputate mattis. Nullam scelerisque nibh vel dui egestas luctus. Vestibulum commodo mi ex. In laoreet hendrerit fringilla.

Praesent vel ex sed dolor fermentum lobortis.""#,
                ),
            ),
            (
                Token::ValueSep,
                Pos {
                    offset: 646,
                    line: 3,
                    col: 475,
                },
                ",",
                None,
            ),
            (
                Token::White,
                Pos {
                    offset: 647,
                    line: 3,
                    col: 476,
                },
                "\n  ",
                None,
            ),
            // Line 4
            (
                Token::Str,
                Pos {
                    offset: 650,
                    line: 4,
                    col: 3,
                },
                r#""👋""#,
                None,
            ),
            (
                Token::NameSep,
                Pos {
                    offset: 656,
                    line: 4,
                    col: 6,
                },
                ":",
                None,
            ),
            (
                Token::White,
                Pos {
                    offset: 657,
                    line: 4,
                    col: 7,
                },
                "   ",
                None,
            ),
            (
                Token::ArrBegin,
                Pos {
                    offset: 660,
                    line: 4,
                    col: 10,
                },
                "[",
                None,
            ),
            (
                Token::Str,
                Pos {
                    offset: 661,
                    line: 4,
                    col: 11,
                },
                r#""🌎""#,
                None,
            ),
            (
                Token::ValueSep,
                Pos {
                    offset: 667,
                    line: 4,
                    col: 14,
                },
                ",",
                None,
            ),
            (
                Token::Str,
                Pos {
                    offset: 668,
                    line: 4,
                    col: 15,
                },
                r#""🌏""#,
                None,
            ),
            (
                Token::ValueSep,
                Pos {
                    offset: 674,
                    line: 4,
                    col: 18,
                },
                ",",
                None,
            ),
            (
                Token::White,
                Pos {
                    offset: 675,
                    line: 4,
                    col: 19,
                },
                " ",
                None,
            ),
            (
                Token::Str,
                Pos {
                    offset: 676,
                    line: 4,
                    col: 20,
                },
                r#""🌏""#,
                None,
            ),
            (
                Token::ValueSep,
                Pos {
                    offset: 682,
                    line: 4,
                    col: 23,
                },
                ",",
                None,
            ),
            (
                Token::White,
                Pos {
                    offset: 683,
                    line: 4,
                    col: 24,
                },
                " ",
                None,
            ),
            (
                Token::Str,
                Pos {
                    offset: 684,
                    line: 4,
                    col: 25,
                },
                r#""こんにちは、世界""#,
                None,
            ),
            (
                Token::ArrEnd,
                Pos {
                    offset: 710,
                    line: 4,
                    col: 35,
                },
                "]",
                None,
            ),
            (
                Token::ValueSep,
                Pos {
                    offset: 711,
                    line: 4,
                    col: 36,
                },
                ",",
                None,
            ),
            (
                Token::White,
                Pos {
                    offset: 712,
                    line: 4,
                    col: 37,
                },
                "\n  ",
                None,
            ),
            // Line 5
            (
                Token::Str,
                Pos {
                    offset: 715,
                    line: 5,
                    col: 3,
                },
                r#""abc\u0020123""#,
                Some(r#""abc 123""#),
            ),
            (
                Token::NameSep,
                Pos {
                    offset: 729,
                    line: 5,
                    col: 17,
                },
                ":",
                None,
            ),
            (
                Token::White,
                Pos {
                    offset: 730,
                    line: 5,
                    col: 18,
                },
                " ",
                None,
            ),
            (
                Token::ObjBegin,
                Pos {
                    offset: 731,
                    line: 5,
                    col: 19,
                },
                "{",
                None,
            ),
            (
                Token::ObjBegin,
                Pos {
                    offset: 732,
                    line: 5,
                    col: 20,
                },
                "{",
                None,
            ),
            (
                Token::ObjBegin,
                Pos {
                    offset: 733,
                    line: 5,
                    col: 21,
                },
                "{",
                None,
            ),
            (
                Token::Str,
                Pos {
                    offset: 734,
                    line: 5,
                    col: 22,
                },
                r#""inner""#,
                None,
            ),
            (
                Token::NameSep,
                Pos {
                    offset: 741,
                    line: 5,
                    col: 29,
                },
                ":",
                None,
            ),
            (
                Token::ArrBegin,
                Pos {
                    offset: 742,
                    line: 5,
                    col: 30,
                },
                "[",
                None,
            ),
            (
                Token::ArrBegin,
                Pos {
                    offset: 743,
                    line: 5,
                    col: 31,
                },
                "[",
                None,
            ),
            (
                Token::ArrBegin,
                Pos {
                    offset: 744,
                    line: 5,
                    col: 32,
                },
                "[",
                None,
            ),
            (
                Token::Num,
                Pos {
                    offset: 745,
                    line: 5,
                    col: 33,
                },
                "-1",
                None,
            ),
            (
                Token::ValueSep,
                Pos {
                    offset: 747,
                    line: 5,
                    col: 35,
                },
                ",",
                None,
            ),
            (
                Token::Num,
                Pos {
                    offset: 748,
                    line: 5,
                    col: 36,
                },
                "-2.0",
                None,
            ),
            (
                Token::ValueSep,
                Pos {
                    offset: 752,
                    line: 5,
                    col: 40,
                },
                ",",
                None,
            ),
            (
                Token::Num,
                Pos {
                    offset: 753,
                    line: 5,
                    col: 41,
                },
                "-3.00e+0",
                None,
            ),
            (
                Token::ValueSep,
                Pos {
                    offset: 761,
                    line: 5,
                    col: 49,
                },
                ",",
                None,
            ),
            (
                Token::Num,
                Pos {
                    offset: 762,
                    line: 5,
                    col: 50,
                },
                "-4E-0",
                None,
            ),
            (
                Token::ValueSep,
                Pos {
                    offset: 767,
                    line: 5,
                    col: 55,
                },
                ",",
                None,
            ),
            (
                Token::Num,
                Pos {
                    offset: 768,
                    line: 5,
                    col: 56,
                },
                "3.141592653589793238462643383279",
                None,
            ),
            (
                Token::ValueSep,
                Pos {
                    offset: 800,
                    line: 5,
                    col: 88,
                },
                ",",
                None,
            ),
            (
                Token::LitNull,
                Pos {
                    offset: 801,
                    line: 5,
                    col: 89,
                },
                "null",
                None,
            ),
            (
                Token::ArrEnd,
                Pos {
                    offset: 805,
                    line: 5,
                    col: 93,
                },
                "]",
                None,
            ),
            (
                Token::ArrEnd,
                Pos {
                    offset: 806,
                    line: 5,
                    col: 94,
                },
                "]",
                None,
            ),
            (
                Token::ArrEnd,
                Pos {
                    offset: 807,
                    line: 5,
                    col: 95,
                },
                "]",
                None,
            ),
            (
                Token::ObjEnd,
                Pos {
                    offset: 808,
                    line: 5,
                    col: 96,
                },
                "}",
                None,
            ),
            (
                Token::ObjEnd,
                Pos {
                    offset: 809,
                    line: 5,
                    col: 97,
                },
                "}",
                None,
            ),
            (
                Token::ObjEnd,
                Pos {
                    offset: 810,
                    line: 5,
                    col: 98,
                },
                "}",
                None,
            ),
            (
                Token::White,
                Pos {
                    offset: 811,
                    line: 5,
                    col: 99,
                },
                "\n",
                None,
            ),
            // Line 6
            (
                Token::ObjEnd,
                Pos {
                    offset: 812,
                    line: 6,
                    col: 1,
                },
                "}",
                None,
            ),
            (
                Token::Eof,
                Pos {
                    offset: 813,
                    line: 6,
                    col: 2,
                },
                "",
                None,
            ),
            (
                Token::Eof,
                Pos {
                    offset: 813,
                    line: 6,
                    col: 2,
                },
                "",
                None,
            ),
            (
                Token::Eof,
                Pos {
                    offset: 813,
                    line: 6,
                    col: 2,
                },
                "",
                None,
            ),
        ];

        let mut an = FixedAnalyzer::new(JSON_TEXT.as_bytes());

        for (i, (expect_token, expect_pos, expect_literal, expect_unescaped)) in
            EXPECT.iter().enumerate()
        {
            let actual_token = an.next();
            let actual_pos = *an.pos();
            let content = an.content();

            assert_eq!(
                *expect_token, actual_token,
                "i = {i}, actual_pos = {actual_pos}, expect_pos = {expect_pos}"
            );
            assert_eq!(
                *expect_pos, actual_pos,
                "i = {i}, token = {actual_token}, content = {content}"
            );
            assert_eq!(
                *expect_literal,
                content.literal(),
                "i = {i}, token = {actual_token}"
            );
            if let Some(u) = expect_unescaped {
                assert!(
                    content.is_escaped(),
                    "i = {i}, token = {actual_token}, literal = {expect_literal:?}"
                );
                assert_eq!(*u, content.unescaped());
            } else {
                assert!(
                    !content.is_escaped(),
                    "i = {i}, token = {actual_token}, literal = {expect_literal:?}"
                );
                assert_eq!(*expect_literal, content.unescaped());
            }
        }
    }

    fn inline_buf<const N: usize>(src: &[u8; N]) -> (u8, InlineBuf) {
        assert!(N <= INLINE_LEN);
        let mut dst = [0; INLINE_LEN];
        dst[0..N].copy_from_slice(src);

        (u8::try_from(N).unwrap(), dst)
    }

    impl From<(u8, InlineBuf)> for InnerContent<Vec<u8>> {
        fn from(value: (u8, InlineBuf)) -> Self {
            InnerContent::Inline(value.0, value.1)
        }
    }
}
