xocomil 0.3.0

A lightweight, zero-allocation HTTP/1.1 request parser and response writer
Documentation
//! Heap-allocation audit for `xocomil::body::BodyReader`.
//!
//! Run with: `cargo test --release --features dhat-heap --test alloc_body_reader`
//!
//! `BodyReader::new` is non-allocating (it just chains a `Cursor` and a
//! reader). `Read::read` has one heap fallback in
//! `read_chunk_size_line_dyn` for chunk-size lines that span multiple
//! `fill_buf` reads — but with `&[u8]` or `BufReader` as the source,
//! the fast path is taken and no allocation occurs. This audit
//! exercises content-length, chunked fast-path, and chunked-with-
//! prefetch in a single test.
//!
//! See `alloc_pct.rs` for the design rationale (one test per binary,
//! delta-based assertions).

#![cfg(feature = "dhat-heap")]

use std::hint::black_box;

use xocomil::body::{BodyKind, BodyReader, UNLIMITED_BODY_SIZE};

#[global_allocator]
static ALLOC: dhat::Alloc = dhat::Alloc;

#[test]
fn body_reader_hot_paths_zero_alloc() {
    let _profiler = dhat::Profiler::builder().testing().build();

    // Build all fixtures first so their Vec allocations are excluded
    // from the measurement window.
    let cl_body = vec![b'x'; 1024];
    let chunked_body = build_chunks(&[b"hello world".as_slice(); 16]);
    let prefetch_body = build_chunks(&[b"hello world".as_slice()]);
    let split = prefetch_body.len() / 2;

    let baseline = dhat::HeapStats::get();

    // -- Content-Length -------------------------------------------------
    for _ in 0..100 {
        let mut src: &[u8] = black_box(&cl_body);
        let mut r = BodyReader::new(
            &mut src,
            b"",
            BodyKind::ContentLength(1024),
            UNLIMITED_BODY_SIZE,
        );
        let mut sink = std::io::sink();
        std::io::copy(&mut r, &mut sink).unwrap();
    }

    // -- Chunked, single-fill fast path ---------------------------------
    for _ in 0..100 {
        let mut src: &[u8] = black_box(&chunked_body);
        let mut r = BodyReader::new(&mut src, b"", BodyKind::Chunked, UNLIMITED_BODY_SIZE);
        let mut sink = std::io::sink();
        std::io::copy(&mut r, &mut sink).unwrap();
    }

    // -- Chunked with prefetch overhang ---------------------------------
    for _ in 0..100 {
        let mut src: &[u8] = black_box(&prefetch_body[split..]);
        let mut r = BodyReader::new(
            &mut src,
            black_box(&prefetch_body[..split]),
            BodyKind::Chunked,
            UNLIMITED_BODY_SIZE,
        );
        let mut sink = std::io::sink();
        std::io::copy(&mut r, &mut sink).unwrap();
    }

    let stats = dhat::HeapStats::get();
    dhat::assert_eq!(stats.total_blocks - baseline.total_blocks, 0);
    dhat::assert_eq!(stats.total_bytes - baseline.total_bytes, 0);
}

fn build_chunks(payloads: &[&[u8]]) -> Vec<u8> {
    let mut out = Vec::new();
    for p in payloads {
        out.extend_from_slice(format!("{:x}\r\n", p.len()).as_bytes());
        out.extend_from_slice(p);
        out.extend_from_slice(b"\r\n");
    }
    out.extend_from_slice(b"0\r\n\r\n");
    out
}