xaac-rs 0.2.0

High-level Rust AAC/xHE-AAC encoder and decoder bindings built on libxaac
Documentation
use std::alloc::{Layout, alloc_zeroed, dealloc};
use std::ffi::CStr;
use std::ptr::NonNull;

use crate::error::{Error, Result};

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct VersionInfo {
    pub name: String,
    pub version: String,
}

pub fn c_string(ptr: *mut i8) -> String {
    if ptr.is_null() {
        return String::new();
    }

    unsafe { CStr::from_ptr(ptr) }
        .to_string_lossy()
        .into_owned()
}

#[derive(Debug)]
pub struct AlignedBuffer {
    base: NonNull<u8>,
    aligned: NonNull<u8>,
    layout: Layout,
}

impl AlignedBuffer {
    pub fn new(size: usize, alignment: usize) -> Result<Self> {
        let alignment = alignment.max(1).next_power_of_two();
        let header_size = 2 * std::mem::size_of::<usize>();
        let total_size = size
            .checked_add(alignment)
            .and_then(|value| value.checked_add(header_size))
            .ok_or(Error::AllocationFailed { size, alignment })?;
        let layout = Layout::from_size_align(total_size, std::mem::align_of::<usize>())
            .map_err(|_| Error::AllocationFailed { size, alignment })?;
        let base = unsafe { alloc_zeroed(layout) };
        let base = NonNull::new(base).ok_or(Error::AllocationFailed { size, alignment })?;
        let aligned_addr =
            (base.as_ptr() as usize + header_size + alignment - 1) & !(alignment - 1);
        let aligned = NonNull::new(aligned_addr as *mut u8)
            .ok_or(Error::AllocationFailed { size, alignment })?;

        Ok(Self {
            base,
            aligned,
            layout,
        })
    }

    pub fn as_ptr(&self) -> *mut u8 {
        self.aligned.as_ptr()
    }
}

impl Drop for AlignedBuffer {
    fn drop(&mut self) {
        unsafe {
            dealloc(self.base.as_ptr(), self.layout);
        }
    }
}

unsafe extern "C" fn xaac_alloc(size: u32, alignment: u32) -> *mut std::ffi::c_void {
    let requested_align = alignment
        .max(libxaac_sys::DEFAULT_MEM_ALIGN_8)
        .next_power_of_two() as usize;
    let header_words = 2 * std::mem::size_of::<usize>();
    let total_size = size as usize + requested_align + header_words;
    let layout =
        Layout::from_size_align(total_size, std::mem::align_of::<usize>()).expect("valid layout");
    let base = unsafe { alloc_zeroed(layout) };
    if base.is_null() {
        return std::ptr::null_mut();
    }

    let aligned_addr =
        (base as usize + header_words + requested_align - 1) & !(requested_align - 1);
    let aligned = aligned_addr as *mut u8;
    let meta = unsafe { aligned.cast::<usize>().sub(2) };
    unsafe {
        meta.write(base as usize);
        meta.add(1).write(total_size);
    }
    aligned.cast()
}

unsafe extern "C" fn xaac_free(ptr: *mut std::ffi::c_void) {
    if ptr.is_null() {
        return;
    }

    let meta = unsafe { (ptr as *mut usize).sub(2) };
    let base = unsafe { meta.read() } as *mut u8;
    let total_size = unsafe { meta.add(1).read() };
    let layout =
        Layout::from_size_align(total_size, std::mem::align_of::<usize>()).expect("valid layout");
    unsafe {
        dealloc(base, layout);
    }
}

pub(crate) fn encoder_alloc() -> Option<unsafe extern "C" fn(u32, u32) -> *mut std::ffi::c_void> {
    Some(xaac_alloc)
}

pub(crate) fn encoder_free() -> Option<unsafe extern "C" fn(*mut std::ffi::c_void)> {
    Some(xaac_free)
}