Protobuf's varint encoding/decoding for LEB128 friendly types with full const context operations supports.
Installation
[]
= "0.14"
no_std is supported by disabling the default std feature:
[]
= { = "0.14", = false }
Quick Start
Encode and decode primitive integers as varints:
use Varint;
// Encode
let mut buf = ;
let len = 300u32.encode.unwrap;
// Decode
let = u32decode.unwrap;
assert_eq!;
assert_eq!;
Signed integers use zigzag encoding, so small absolute values stay compact:
use Varint;
let mut buf = ;
let len = .encode.unwrap;
assert_eq!; // -1 encodes to a single byte
Encode/decode sequences:
use ;
let values: = .collect;
let encoded_len = encoded_sequence_len;
let mut buf = vec!;
encode_sequence.unwrap;
let = .unwrap;
assert_eq!;
All encoding and decoding functions are available in const context as free functions:
use ;
const ENCODED: Buffer = encode_u32_varint;
const VALUE: u32 = ;
assert_eq!;
assert_eq!;
Optional Features
The following feature flags enable varint support for types from third-party crates. Multiple versions of the same crate can be enabled simultaneously.
| Feature | Crate | Notes |
|---|---|---|
arbitrary-int (= v2) |
arbitrary-int v1 |
Unsigned types + signed types (u1..u127, i1..i127) |
arbitrary-int_1 |
arbitrary-int v1 |
Unsigned (u1..u127) |
bnum (= v0.13) |
bnum |
|
chrono (= v0.4) |
chrono |
Not fully const-compatible |
chrono-tz (= v0.10) |
chrono-tz |
|
ethereum-types (= v0.16) |
ethereum-types |
|
ethereum-types_0_16 |
ethereum-types v0.16 |
|
ethereum-types_0_15 |
ethereum-types v0.15 |
|
float8 (= v0.4) |
float8 |
|
half (= v2) |
half |
|
num-complex (= v0.4) |
num-complex |
|
num-rational (= v0.4) |
num-rational |
|
primitive-types (= v0.14) |
primitive-types |
|
primitive-types_0_14 |
primitive-types v0.14 |
|
primitive-types_0_13 |
primitive-types v0.13 |
|
ruint (= v1) |
ruint |
Not const-compatible |
time (= v0.3) |
time |
Benchmarks
varing's varint encode/decode is competitive with the fastest Rust varint
crates and at parity with prost — protobuf's own
varint implementation.
The tables below show single-value and bulk (1000-value sweep) timings for
u64 (all protobuf varints are u64), lower is better. Figures are the
median of one cargo bench run on Apple Silicon (aarch64) and are only
meaningful relative to each other on the same machine — reproduce with
cargo bench.
Encode u64
| value | varing |
prost |
integer-encoding |
unsigned-varint |
leb128 |
|---|---|---|---|---|---|
| 1 byte | 527 ps | 1.20 ns | 542 ps | 874 ps | 429 ps |
| 10 bytes | 2.16 ns | 6.00 ns | 2.14 ns | 2.34 ns | 2.14 ns |
| mixed ×1000 | 2.44 µs | 2.29 µs | 2.51 µs | 3.91 µs | 9.13 µs |
Decode u64
| value | varing |
prost |
integer-encoding |
unsigned-varint |
leb128 |
|---|---|---|---|---|---|
| 1 byte | 386 ps | 749 ps | 775 ps | 573 ps | 524 ps |
| 10 bytes | 1.96 ns | 1.71 ns | 2.76 ns | 2.28 ns | 5.35 ns |
| mixed ×1000 | 2.06 µs | 2.29 µs | 1.86 µs | 2.04 µs | 3.44 µs |
On the bulk sweep varing is within ~10% of prost in both directions
(faster on decode, slightly slower on encode); on single values it is
substantially faster than prost, whose figures include its bytes::BufMut
API overhead. prost appears only in the u64 groups because that is the
only varint width it exposes; leb128 is u64-only for unsigned values and
uses a different signed format, so it is excluded from the signed groups. The
full suite in benches/varint.rs also covers u16,
u32, u128, and zigzag i64.
Testing
- Property-based testing with
quickcheckfor all types (including optional features). - Fuzz testing with
cargo fuzzfor all types.
License
varing is under the terms of both the MIT license and the
Apache License (Version 2.0).
See LICENSE-APACHE, LICENSE-MIT for details.
Copyright (c) 2026 Al Liu.