Repository: swc-project/swc
File: crates/swc_common/src/input.rs (Rust)

Code before:
    /// changed in the future.
    ///
    /// # Safety
    /// - start should be less than or equal to end.
    /// - start and end should be in the valid range of input.
    pub unsafe fn slice_to_cur(&mut self, start: BytePos) -> &'a str {
        let end = self.last_pos;

        debug_assert!(start <= end, "Cannot slice {start:?}..{end:?}");
        let s = self.orig;

        let start_idx = (start - self.orig_start).0 as usize;
        let end_idx = (end - self.orig_start).0 as usize;

        debug_assert!(end_idx <= s.len());

        let ret = unsafe { s.get_unchecked(start_idx..end_idx) };

        self.iter = unsafe { s.get_unchecked(end_idx..) }.chars();

        ret
    }

    #[inline]
    pub fn bump_bytes(&mut self, n: usize) {
        unsafe {
            // Safety: We only proceed, not go back.
            self.reset_to(self.last_pos + BytePos(n as u32));
        }
    }

    pub fn start_pos(&self) -> BytePos {
        self.orig_start
    }

    #[inline(always)]
    pub fn end_pos(&self) -> BytePos {
        self.orig_end
    }
}

/// Creates an [Input] from [SourceFile]. This is an alias for
///
/// ```ignore
///    StringInput::new(&fm.src, fm.start_pos, fm.end_pos)
/// ```
impl<'a> From<&'a SourceFile> for StringInput<'a> {
    fn from(fm: &'a SourceFile) -> Self {
        StringInput::new(&fm.src, fm.start_pos, fm.end_pos)
    }
}

Review comment (bug):
The refactored `bump_bytes` implementation uses byte-level slicing with `get_unchecked(n..)` but does not validate that `n` falls on a UTF-8 character boundary. This can cause panics when creating the `chars()` iterator if `n` splits a multi-byte character. The previous `reset_to` implementation avoided this by calculating positions from the original string. Consider adding a debug assertion like `debug_assert!(s.is_char_boundary(n))` or document that callers must ensure `n` is on a character boundary.
```suggestion
        let s = self.iter.as_str();
        debug_assert!(s.is_char_boundary(n), "bump_bytes: n={} is not a char boundary in {:?}", n, s);
```

Code after:
    /// changed in the future.
    ///
    /// # Safety
    /// - start should be less than or equal to end.
    /// - start and end should be in the valid range of input.
    pub unsafe fn slice_to_cur(&mut self, start: BytePos) -> &'a str {
        let end = self.last_pos;

        debug_assert!(start <= end, "Cannot slice {start:?}..{end:?}");
        let s = self.orig;

        let start_idx = (start - self.orig_start).0 as usize;
        let end_idx = (end - self.orig_start).0 as usize;

        debug_assert!(end_idx <= s.len());

        let ret = unsafe { s.get_unchecked(start_idx..end_idx) };

        self.iter = unsafe { s.get_unchecked(end_idx..) }.chars();

        ret
    }

    #[inline]
    pub fn bump_bytes(&mut self, n: usize) {
        let s = self.iter.as_str();
        self.iter = unsafe { s.get_unchecked(n..) }.chars();
        self.last_pos.0 += n as u32;
    }

    #[inline]
    pub fn bump_one(&mut self) {
        if self.iter.next().is_some() {
            self.last_pos.0 += 1;
        } else {
            unsafe {
                debug_unreachable!("bump should not be called when cur() == None");
            }
        }
    }

    pub fn start_pos(&self) -> BytePos {
        self.orig_start
    }

    #[inline(always)]
    pub fn end_pos(&self) -> BytePos {
        self.orig_end
    }
}