Fix panic when parsing with a multi-byte delimiter - #61
Conversation
Ini::parse split the value using delimiter + 1, assuming the delimiter occupies a single byte. With a multi-byte UTF-8 delimiter this offset lands inside the delimiter character, and slicing the string there panics. Advance by the delimiter's actual UTF-8 length instead.
12c5dbd to
1842f3f
Compare
There was a problem hiding this comment.
🟡 Changes recommended
The change is a correct, well-tested, narrowly-scoped bug fix, but repository conventions favor conservative human sign-off for parser-behavior changes affecting the public API.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR fixes a panic in Ini::parse that occurs when a user configures a multi-byte UTF-8 delimiter (via the public IniDefault::delimiters field) and a parsed line uses it. The previous code sliced the value with trimmed[delimiter + 1..], hard-coding a single-byte advance past the delimiter; for a multi-byte delimiter, delimiter + 1 lands inside the character and slicing on a non-char boundary panics. The fix advances by the delimiter's actual UTF-8 length.
Changes:
- Compute the delimiter's real UTF-8 length via
trimmed[delimiter..].chars().next().map_or(1, char::len_utf8)and slice the value atdelimiter + delimiter_len. - Add a regression test
multibyte_delimiter_does_not_panicusing a two-byte delimiter (\u{00a7}).
File summaries
| File | Description |
|---|---|
| src/ini.rs | Advances past the matched delimiter by its UTF-8 byte length instead of a hard-coded 1, preventing a panic on non-char-boundary slicing. |
| tests/test.rs | Adds a regression test that parses a line with a two-byte delimiter and asserts the value is read back correctly. |
The fix is correct: trimmed.find(&self.delimiters[..]) returns the byte offset of the start of the matched delimiter char, which is always a valid char boundary, so chars().next() reliably yields that delimiter char and char::len_utf8 gives the exact number of bytes to skip. For single-byte (ASCII) delimiters like the default = and :, the behavior is unchanged. I confirmed there are no other delimiter + 1 slicing sites, and the test follows the file's existing conventions.
Review details
- Files reviewed: 2/2 changed files
- Comments generated: 0
- Review effort level: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
Thanks for your contribution @dualfroz Merged. |
Problem
Parsing input panics when a configured delimiter is a multi-byte UTF-8
character and a line uses that delimiter:
delimitersis a public, user-configurable field, so this is reachable throughthe public API with safe input.
Root cause
In
Ini::parse(src/ini.rs), the key/value split computes the delimiterposition with
str::find, which returns a byte offset, and then slices thevalue starting one byte later:
delimiter + 1assumes the delimiter occupies a single byte. When thedelimiter is a multi-byte character,
delimiter + 1lands inside thatcharacter, and slicing a
stron a non-char-boundary index panics. ASCIIdelimiters such as the default
=and:happen to be one byte, which is whythis went unnoticed.
Fix
Advance past the delimiter by its actual UTF-8 length instead of a hard-coded
1:For single-byte delimiters this is identical to the previous behaviour.
Test
Added
multibyte_delimiter_does_not_panictotests/test.rs, which configuresa two-byte delimiter (
\u{00a7}), parseskey\u{00a7}value, and asserts thevalue is read back correctly instead of panicking.