ppp 2.3.0

A Proxy Protocol Parser written in Rust. See HAProxy for the protocol specification.
Documentation
use criterion::{criterion_group, criterion_main};
use criterion::{BenchmarkId, Criterion};

#[cfg(unix)]
use pprof::criterion::{Output, PProfProfiler};

use ppp::v2;

fn ipv6_input() -> Vec<u8> {
    let prefix = b"\r\n\r\n\0\r\nQUIT\n";
    let mut input: Vec<u8> = Vec::with_capacity(prefix.len());

    input.extend_from_slice(prefix);
    input.push(0x21);
    input.push(0x21);
    input.extend(&[0, 45]);
    input.extend(&[
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
        0xFF,
    ]);
    input.extend(&[
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
        0xF1,
    ]);
    input.extend(&[0, 80]);
    input.extend(&[1, 187]);
    input.extend(&[1, 0, 1, 5]);
    input.extend(&[2, 0, 2, 5, 5]);

    input
}

fn ipv4_input() -> Vec<u8> {
    let prefix = b"\r\n\r\n\0\r\nQUIT\n";
    let mut input: Vec<u8> = Vec::with_capacity(prefix.len());

    input.extend_from_slice(prefix);
    input.push(0x21);
    input.push(0x11);
    input.extend(&[0, 26]);
    input.extend(&[127, 0, 0, 1]);
    input.extend(&[198, 168, 1, 1]);
    input.extend(&[0, 80]);
    input.extend(&[1, 187]);
    input.extend(&[1, 0, 1, 5]);
    input.extend(&[2, 0, 2, 5, 5]);
    input.extend(&[2, 0, 2, 5, 5]);

    input
}

fn benchmarks(c: &mut Criterion) {
    let mut group = c.benchmark_group("PPP Binary");

    let inputs = [
        ("IPv4 with TLVs", ipv4_input()),
        ("IPv6 without TLVs", ipv6_input()),
    ];

    for (id, input) in inputs {
        group.bench_with_input(
            BenchmarkId::new("v2::Header::try_from", id),
            input.as_slice(),
            |b, i| {
                b.iter(|| v2::Header::try_from(i).unwrap());
            },
        );

        group.bench_with_input(
            BenchmarkId::new("v2::Header::as_bytes", id),
            &v2::Header::try_from(input.as_slice()).unwrap(),
            |b, h| {
                b.iter(|| h.as_bytes());
            },
        );
    }

    let source_address = [
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
        0xF2,
    ];
    let destination_address = [
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
        0xF1,
    ];
    let addresses =
        v2::Addresses::IPv6(v2::IPv6::new(source_address, destination_address, 80, 443));

    group.bench_with_input(
        BenchmarkId::new("v2::Builder::build", "IPv6 with TLVs"),
        &addresses,
        |b, a| {
            b.iter(|| {
                v2::Builder::new(
                    v2::Version::Two | v2::Command::Local,
                    v2::AddressFamily::IPv6 | v2::Protocol::Unspecified,
                )
                .write_payload(a)
                .unwrap()
                .write_payloads(vec![(v2::Type::NoOp, [0].as_slice())])
                .unwrap()
                .write_tlv(v2::Type::NoOp, [42].as_slice())
                .unwrap()
                .build()
                .unwrap()
            })
        },
    );

    group.bench_with_input(
        BenchmarkId::new("v2::Builder::build", "IPv6 with TLVs with length"),
        &addresses,
        |b, &a| {
            b.iter(|| {
                v2::Builder::with_addresses(
                    v2::Version::Two | v2::Command::Local,
                    v2::Protocol::Unspecified,
                    a,
                )
                .reserve_capacity(8)
                .write_payloads([
                    (v2::Type::NoOp, [0].as_slice()),
                    (v2::Type::NoOp, [42].as_slice()),
                ])
                .unwrap()
                .build()
                .unwrap()
            })
        },
    );

    group.finish();
}

#[cfg(unix)]
criterion_group! {
    name = benches;
    config = {
        Criterion::default().with_profiler(PProfProfiler::new(100, Output::Protobuf))
    };
    targets = benchmarks
}

#[cfg(not(unix))]
criterion_group!(benches, benchmarks);

criterion_main!(benches);