ruapc-bufpool

Crates.ioruapc-bufpool
lib.rsruapc-bufpool
version0.1.0
created_at2026-01-22 12:03:17.523588+00
updated_at2026-01-22 12:03:17.523588+00
descriptionA high-performance memory pool using buddy memory allocation algorithm for efficient fixed-size buffer management
homepage
repositoryhttps://github.com/SF-Zhou/ruapc-bufpool
max_upload_size
id2061420
size100,573
(SF-Zhou)

documentation

README

ruapc-bufpool

Crates.io Documentation License Build Status

A high-performance memory pool using the buddy memory allocation algorithm for efficient fixed-size buffer management. This crate is part of the ruapc project.

Features

  • Buddy Memory Allocation: Efficiently manages memory in 64 MiB blocks, supporting allocation of 1 MiB, 4 MiB, 16 MiB, and 64 MiB buffers
  • Both Sync and Async APIs: Designed for tokio environments with async-first design using tokio::sync::Mutex
  • Automatic Memory Reclamation: Buffers are automatically returned to the pool when dropped with O(1) deallocation
  • Memory Limits: Configurable maximum memory usage with async waiting when limits are reached
  • Custom Allocators: Pluggable allocator trait for custom memory allocation backends
  • O(1) Buddy Merging: Intrusive doubly-linked list with O(1) free/merge operations
  • Zero Copy: Direct memory access through Buffer type that implements Deref<Target = [u8]>
  • Memory Efficient: Buffer struct optimized to 32 bytes using packed fields

Installation

Add this to your Cargo.toml:

[dependencies]
ruapc-bufpool = "0.1"

Quick Start

Synchronous Allocation

use ruapc_bufpool::BufferPoolBuilder;

fn main() -> std::io::Result<()> {
    // Create a buffer pool with 256 MiB max memory (default)
    // Returns Arc<BufferPool> for safe sharing across threads
    let pool = BufferPoolBuilder::new()
        .max_memory(256 * 1024 * 1024)
        .build();

    // Allocate a 1 MiB buffer
    let mut buffer = pool.allocate(1024 * 1024)?;
    
    // Write to the buffer
    buffer[0] = 42;
    buffer[1] = 43;
    
    // Read from the buffer
    assert_eq!(buffer[0], 42);
    
    // Buffer is automatically returned to the pool when dropped
    drop(buffer);
    
    Ok(())
}

Asynchronous Allocation

use ruapc_bufpool::BufferPoolBuilder;

#[tokio::main]
async fn main() -> std::io::Result<()> {
    let pool = BufferPoolBuilder::new()
        .max_memory(128 * 1024 * 1024)
        .build();

    // Allocate asynchronously - will wait if memory limit is reached
    let buffer = pool.async_allocate(4 * 1024 * 1024).await?;
    
    assert!(buffer.len() >= 4 * 1024 * 1024);
    
    Ok(())
}

Custom Allocator

use ruapc_bufpool::{Allocator, BufferPoolBuilder};
use std::io::Result;

struct MyAllocator;

impl Allocator for MyAllocator {
    fn allocate(&self, size: usize) -> Result<*mut u8> {
        // Custom allocation logic
        // ...
        # unimplemented!()
    }

    unsafe fn deallocate(&self, ptr: *mut u8, size: usize) {
        // Custom deallocation logic
        // ...
    }
}

fn main() {
    let pool = BufferPoolBuilder::new()
        .allocator(Box::new(MyAllocator))
        .build();
}

Architecture

Buddy Memory Allocation

The pool uses a buddy memory allocation algorithm with 4 levels:

Level Size Blocks per 64 MiB
0 1 MiB 64
1 4 MiB 16
2 16 MiB 4
3 64 MiB 1

When allocating:

  1. Find the smallest level that can satisfy the request
  2. If no free buffer at that level, look for a larger one
  3. Split larger buffers into 4 smaller ones as needed
  4. Return the first available buffer

When freeing:

  1. Mark the buffer as free
  2. Check if all 4 sibling buffers are free
  3. If so, merge them into a larger buffer
  4. Repeat until no more merging is possible

Memory Management

  • Each 64 MiB block maintains a bit-packed state array (22 bytes for 85 nodes)
  • Free lists use intrusive doubly-linked lists for O(1) operations
  • Buffer holds Arc<BufferPool> reference, ensuring pool validity
  • Buffer drop directly acquires lock and deallocates with O(1) operation

Thread Safety

  • Uses tokio::sync::Mutex for synchronization
  • Sync allocation uses blocking_lock()
  • Async allocation uses lock().await
  • Buffer drop handles both sync and async contexts intelligently

Buffer Memory Layout

The Buffer struct is optimized for minimal memory footprint (32 bytes):

  • ptr: 8 bytes (pointer to memory)
  • pool: 8 bytes (Arc reference to pool)
  • block: 8 bytes (pointer to buddy block)
  • level: 1 byte (0-3, only 4 possible values)
  • index: 1 byte (0-63, max index at level 0)
  • Padding: 6 bytes for alignment

Performance Considerations

  • Allocation: O(log n) where n is the number of levels (constant in practice)
  • Deallocation: O(log n) for merging, O(1) for just freeing
  • Memory overhead: ~22 bytes state array + free list nodes per 64 MiB block
  • Best for: Large buffers (1+ MiB), frequent allocation/deallocation patterns

API Reference

See the documentation on docs.rs for detailed API reference.

License

Licensed under either of:

at your option.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Commit count: 2

cargo fmt