Complete out-of-range varint is reported as UnexpectedEof
Description
A complete varint outside the target integer range produces UnexpectedEof, even though a trailing value is available. The reader documentation instead describes truncation of out-of-range values.
Steps to Reproduce
Cargo.toml:
[package]
name = "integer-encoding-repro"
version = "0.1.0"
edition = "2021"
[dependencies]
integer-encoding = "=4.1.0"
src/main.rs:
use integer_encoding::VarIntReader;
use std::io::{Cursor, ErrorKind};
fn main() {
let mut input = Cursor::new([0xac, 0x02, 0x2a]);
let err = input.read_varint::<u8>().unwrap_err();
assert_eq!(err.kind(), ErrorKind::UnexpectedEof);
assert_eq!(input.position(), 2);
assert_eq!(input.read_varint::<u8>().unwrap(), 42);
}
Run cargo run.
Expected Behavior
ac 02 is the complete varint for 300. The documentation describes truncation for values outside the target range and associates UnexpectedEof with EOF. It does not specify a particular alternative error kind for range rejection.
Actual Behavior
The assertions pass: the reader consumes the complete value, returns UnexpectedEof, and leaves 42 available. No integer is returned. The reader maps a failed decode to UnexpectedEof, including a numeric range rejection. This two-byte input fits the u8 stream byte limit, so it is distinct from the width-cap framing issue.
The reproducer passed in debug and release with Rust 1.91.1 on macOS arm64 and default features. Later versions have not been checked.
Change history
- v1 · created · 2026-10-28 13:59 UTC
{ "title": "Complete out-of-range varint is reported as UnexpectedEof", "body": "## Description\n\nA complete varint outside the target integer range produces `UnexpectedEof`, even though a trailing value is available. The reader documentation instead describes truncation of out-of-range values.\n\n## Steps to Reproduce\n\n`Cargo.toml`:\n\n```toml\n[package]\nname = \"integer-encoding-repro\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\ninteger-encoding = \"=4.1.0\"\n```\n\n`src/main.rs`:\n\n```rust\nuse integer_encoding::VarIntReader;\nuse std::io::{Cursor, ErrorKind};\n\nfn main() {\n let mut input = Cursor::new([0xac, 0x02, 0x2a]);\n let err = input.read_varint::<u8>().unwrap_err();\n assert_eq!(err.kind(), ErrorKind::UnexpectedEof);\n assert_eq!(input.position(), 2);\n assert_eq!(input.read_varint::<u8>().unwrap(), 42);\n}\n```\n\nRun `cargo run`.\n\n## Expected Behavior\n\n`ac 02` is the complete varint for 300. The [documentation](https://github.com/dermesser/integer-encoding-rs/blob/e0a10d01e86a38d0df177676c62f9a10c845ab53/src/reader.rs#L17-L23) describes truncation for values outside the target range and associates `UnexpectedEof` with EOF. It does not specify a particular alternative error kind for range rejection.\n\n## Actual Behavior\n\nThe assertions pass: the reader consumes the complete value, returns `UnexpectedEof`, and leaves 42 available. No integer is returned. The [reader](https://github.com/dermesser/integer-encoding-rs/blob/e0a10d01e86a38d0df177676c62f9a10c845ab53/src/reader.rs#L94-L114) maps a failed decode to `UnexpectedEof`, including a numeric range rejection. This two-byte input fits the u8 stream byte limit, so it is distinct from the width-cap framing issue.\n\nThe reproducer passed in debug and release with Rust 1.91.1 on macOS arm64 and default features. Later versions have not been checked.", "validity": "reported", "upstream_status": "not_reported" }