#varint #zigzag #no-std

no-std tiny-varint

A no_std compatible VarInt encoding/decoding Rust library

1 unstable release

new 0.1.0 Mar 25, 2025

#228 in Embedded development

MIT/Apache

44KB
723 lines

tiny-varint

crates.io Rust Version License no_std

A dependency-free, high-performance variable-length integer (VarInt) encoding/decoding Rust library, fully compatible with no_std environments.

Features

  • Depends only on zigzag-rs: Supports #![no_std] environments
  • Full Type Support: Supports all Rust native integer types
  • High-performance Implementation: Optimized critical paths for resource-constrained scenarios
  • Iterator-based Interface: No dynamic memory allocation required, suitable for embedded systems
  • Zigzag Encoding: Efficiently handles signed integers
  • Rich Error Handling: Detailed error types and messages

Installation

[dependencies]
tiny-varint = "0.1.0"

Feature Overview

Core Functions

Feature Function Name Description
Generic Encoding encode<T: VarInt>() Encodes any integer type to varint
Generic Decoding decode<T: VarInt>() Decodes a varint to any integer type
ZigZag Encoding encode_zigzag() Encodes signed integers using zigzag
ZigZag Decoding decode_zigzag() Decodes zigzag-encoded signed integers
Batch Processing VarIntEncoder/VarIntDecoder Batch encodes/decodes integer arrays
Iterator-based Encoding bytes_of() Iterator-based encoding method
Iterator-based Decoding values_from() Iterator-based decoding method

Direct Usage with encode/decode

The simplest way to use tiny-varint is with the generic encode and decode functions:

use tiny_varint::{encode, decode};

// Encode an unsigned integer
let mut buffer = [0u8; 10];
let bytes_written = encode(42u64, &mut buffer).unwrap();
println!("Encoded 42 using {} bytes", bytes_written);

// Decode it back
let (decoded_value, bytes_read) = decode::<u64>(&buffer[..bytes_written]).unwrap();
assert_eq!(decoded_value, 42u64);

// Works with any integer type - even signed integers
let bytes_written = encode(-123i32, &mut buffer).unwrap();
let (decoded_value, bytes_read) = decode::<i32>(&buffer[..bytes_written]).unwrap();
assert_eq!(decoded_value, -123i32);

Using Encoders/Decoders

use tiny_varint::{VarIntEncoder, VarIntDecoder};

// Encoder example
let mut buffer = [0u8; 100];
let mut encoder = VarIntEncoder::new(&mut buffer);

// Write multiple values
encoder.write(123u64)?;
encoder.write(456u32)?;
encoder.write_zigzag(-789i32)?;

// Get encoded bytes
let bytes_written = encoder.position();

// Decoder example
let mut decoder = VarIntDecoder::new(&buffer[..bytes_written]);

// Read values
let v1 = decoder.read::<u64>()?; // 123
let v2 = decoder.read::<u32>()?; // 456
let v3 = decoder.read_zigzag::<i32>()?; // -789

Using Iterator-based Methods

use tiny_varint::{bytes_of, values_from};

// Encoding iterator - No buffer pre-allocation needed
let value = 16384u64;
for byte in bytes_of(value) {
    // Process each byte individually
    println!("{:02X}", byte);
}

// Decoding iterator - Decode from any byte source
let buffer = [0x80, 0x80, 0x01]; // Encoded value 16384
for result in values_from::<u64>(&buffer) {
    match result {
        Ok(value) => println!("Decoded value: {}", value),
        Err(e) => println!("Decoding error: {:?}", e),
    }
}

Performance Optimizations

tiny-varint has been optimized for various use cases:

  • Avoid Boundary Checks: Pre-validate buffer sizes to reduce in-loop checks
  • Inline Critical Functions: Critical paths marked with #[inline(always)]
  • Optimized Size Calculation: Use lookup-based approach to quickly determine encoding size
  • Batch Processing Optimization: Batch operations avoid multiple boundary checks
  • Iterator-based Design: Directly operate on provided buffers without copying data

Example Code

Basic Encoding/Decoding

use tiny_varint::{encode, decode};

// Encode a single u64 value
let mut buffer = [0u8; 10];
let bytes_written = encode(123u64, &mut buffer)?;
println!("Encoded using {} bytes", bytes_written);

// Decode a single u64 value
let (value, bytes_read) = decode::<u64>(&buffer)?;
println!("Decoded value: {}, used {} bytes", value, bytes_read);

// Works with any integer type
let bytes_written = encode(42i32, &mut buffer)?;
let (value, bytes_read) = decode::<i32>(&buffer)?;
assert_eq!(value, 42i32);

ZigZag Encoding Signed Integers

use tiny_varint::{encode_zigzag, decode_zigzag};

// Encode an i32 value
let mut buffer = [0u8; 10];
let bytes_written = encode_zigzag(-123i32, &mut buffer)?;

// Decode back to i32
let (value, bytes_read) = decode_zigzag::<i32>(&buffer)?;
assert_eq!(value, -123);

Batch Processing

use tiny_varint::{VarIntEncoder, VarIntDecoder};

// Encode multiple values
let values = [1u64, 10, 100, 1000, 10000];
let mut buffer = [0u8; 50];
let mut encoder = VarIntEncoder::new(&mut buffer);
let bytes_written = encoder.write_batch(&values)?;

// Decode multiple values
let mut decoded = [0u64; 5];
let mut decoder = VarIntDecoder::new(&buffer[..bytes_written]);
decoder.read_batch(&mut decoded)?;
assert_eq!(values, decoded);

Complete Examples

Check the examples directory for more examples:

License

Dual-licensed under MIT/Apache-2.0 licenses.

Dependencies

~23KB