ax-codec-bytes 0.1.5

Buffer pooling and bytes crate integration for zero-copy operations and memory-efficient encoding
Documentation

ax-codec-bytes

Buffer pooling and integration with the bytes crate for ax-codec. This crate provides memory-efficient buffer management and zero-copy operations with bytes::Bytes.

Features

  • Thread-local buffer pooling - Reduces allocations for small buffers
  • bytes crate integration - Zero-copy operations with Bytes and BytesMut
  • smallvec integration - Stack-allocated small vectors (optional)
  • no_std compatible (with alloc feature)

Installation

Add to your Cargo.toml:

[dependencies]
ax-codec-bytes = { version = "0.1", features = ["std"] }

Feature Flags

  • std - Enables std library support (default)
  • bytes - Enables integration with bytes crate
  • smallvec - Enables integration with smallvec crate

Thread-Local Buffer Pool

BufferPool

Thread-local pool for reusing buffers:

use ax_codec_bytes::{BufferPool, PooledWriter};
use std::sync::Arc;

let pool = Arc::new(BufferPool::new());
let mut writer = PooledWriter::with_pool(pool.clone());

writer.write_all(b"hello").unwrap();
let data = writer.finish(); // Takes ownership, doesn't recycle

// Or recycle explicitly:
let mut writer = PooledWriter::with_pool(pool.clone());
writer.write_all(b"world").unwrap();
writer.recycle(); // Returns buffer to pool

Custom Pool Configuration

use ax_codec_bytes::BufferPool;

let pool = BufferPool::with_capacity(
    64,    // max buffers in pool
    4096   // default buffer size
);

Pool Statistics

use ax_codec_bytes::BufferPool;

let pool = BufferPool::new();
println!("Pool size: {}", pool.len());
println!("Pool empty: {}", pool.is_empty());

bytes Crate Integration

BytesReader

Zero-copy reader for bytes::Bytes:

use ax_codec_bytes::BytesReader;
use ax_codec_core::{Decode, View};
use bytes::Bytes;

let bytes = Bytes::from(b"\x2a\x00\x00\x00");
let mut reader = BytesReader::new(&bytes);

let value: u32 = Decode::decode(&mut reader).unwrap();
println!("Decoded: {}", value);

BytesMutWriter

Writer for bytes::BytesMut:

use ax_codec_bytes::BytesMutWriter;
use ax_codec_core::Encode;

let mut writer = BytesMutWriter::with_capacity(1024);
42u32.encode(&mut writer).unwrap();

let bytes = writer.freeze(); // Convert to immutable Bytes

Decode from Bytes

Helper function for decoding directly from Bytes:

use ax_codec_core::bytes_impl;
use bytes::Bytes;

let mut buf = Bytes::from(&[0x2a, 0x00, 0x00, 0x00][..]);
let value: u32 = bytes_impl::decode_from_bytes(&mut buf).unwrap();

Validate from Bytes

Validate wire format without allocation:

use ax_codec_core::bytes_impl;
use bytes::Bytes;

let mut buf = Bytes::from(&[0x2a, 0x00, 0x00, 0x00][..]);
bytes_impl::validate_from_bytes::<u32>(&mut buf).unwrap();

smallvec Integration

SmallVecBuf

Stack-allocated small vectors for zero-allocation small collections:

use ax_codec_bytes::SmallVecBuf;
use ax_codec_core::{Encode, Decode};
use ax_codec_core::buffer::{VecWriter, SliceReader};

// Encode
let mut writer = VecWriter::new();
let data: SmallVecBuf<[u8; 4]> = SmallVecBuf::from(vec![1, 2, 3]);
data.encode(&mut writer).unwrap();

// Decode
let mut reader = SliceReader::new(writer.as_slice());
let decoded: SmallVecBuf<[u8; 4]> = Decode::decode(&mut reader).unwrap();

Memory Management

Automatic Recycling

PooledWriter automatically recycles its buffer on drop if not explicitly finished:

use ax_codec_bytes::{BufferPool, PooledWriter};
use std::sync::Arc;

let pool = Arc::new(BufferPool::new());

{
    let mut writer = PooledWriter::with_pool(pool.clone());
    writer.write_all(b"temp data").unwrap();
    // Buffer automatically recycled here
}

assert_eq!(pool.len(), 1); // Buffer returned to pool

Manual Control

Take ownership of the buffer instead of recycling:

use ax_codec_bytes::{BufferPool, PooledWriter};
use std::sync::Arc;

let pool = Arc::new(BufferPool::new());
let mut writer = PooledWriter::with_pool(pool.clone());
writer.write_all(b"important data").unwrap();

let data = writer.finish(); // Takes ownership, doesn't recycle
assert_eq!(pool.len(), 0); // Buffer not returned to pool

Performance Considerations

When to Use Pooling

Buffer pooling is beneficial when:

  • Creating many small buffers (≤ 64 KB)
  • Buffers have similar lifetimes
  • You want to reduce heap allocations

When Not to Use Pooling

Avoid pooling when:

  • Buffers are very large (> 64 KB)
  • Buffers need to be shared across threads
  • Memory pressure is a concern (pool holds onto memory)

Thread Safety

  • BufferPool uses Arc<Mutex<>> for thread-safe access
  • Each thread has its own thread-local pool in ax-codec-core
  • Choose based on your use case:
    • ax-codec-bytes::BufferPool - Thread-safe, shared pool
    • ax-codec-core::pool - Thread-local pool (faster, not shared)

Examples

Zero-Copy with Bytes

use ax_codec_bytes::BytesReader;
use ax_codec_core::View;
use bytes::Bytes;

#[derive(ax_codec_derive::View)]
struct Message<'a> {
    id: u32,
    text: &'a str,
}

let bytes = Bytes::from(&[0x2a, 0x00, 0x00, 0x00, 0x05, b'h', b'e', b'l', b'l', b'o'][..]);
let mut reader = BytesReader::new(&bytes);
let msg = Message::view(&mut reader).unwrap();
println!("ID: {}, Text: {}", msg.id, msg.text);

Efficient Network Buffering

use ax_codec_bytes::{BufferPool, PooledWriter};
use ax_codec_core::BufferWriter;
use std::sync::Arc;

let pool = Arc::new(BufferPool::with_capacity(32, 8192));

// Reuse buffers for multiple messages
for i in 0..100 {
    let mut writer = PooledWriter::with_pool(pool.clone());
    write_message(i, &mut writer).unwrap();
    
    // Send the buffer
    let data = writer.finish();
    send_to_network(data);
}

License

MIT