use crate::{raw, AtmiCtx, AtmiResult};
use core::ffi::{c_char, c_long};
use std::ffi::CStr;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TpTypeInfo {
pub size: usize,
pub type_name: String,
pub subtype: String,
}
#[derive(Debug)]
pub struct TypedBuffer<'ctx> {
ptr: *mut c_char,
pub(crate) ctx: &'ctx AtmiCtx,
owned: bool,
len: usize,
}
impl<'ctx> TypedBuffer<'ctx> {
pub(crate) unsafe fn from_raw(ctx: &'ctx AtmiCtx, raw: *mut c_char) -> Self {
Self {
ptr: raw,
ctx,
owned: true,
len: 0,
}
}
pub(crate) unsafe fn from_raw_with_len(
ctx: &'ctx AtmiCtx,
raw: *mut c_char,
len: usize,
) -> Self {
Self {
ptr: raw,
ctx,
owned: true,
len,
}
}
pub(crate) unsafe fn borrowed_from_raw(ctx: &'ctx AtmiCtx, raw: *mut c_char) -> Self {
Self {
ptr: raw,
ctx,
owned: false,
len: 0,
}
}
pub(crate) fn into_raw(self) -> *mut c_char {
let ptr = self.ptr;
std::mem::forget(self);
ptr
}
#[inline]
pub(crate) fn as_ptr(&self) -> *mut c_char {
self.ptr
}
pub(crate) unsafe fn move_to_context<'new>(self, new_ctx: &'new AtmiCtx) -> TypedBuffer<'new> {
let len = self.len;
TypedBuffer::from_raw_with_len(new_ctx, self.into_raw(), len)
}
#[inline]
pub(crate) fn replace_ptr(&mut self, new_ptr: *mut c_char) {
self.ptr = new_ptr;
}
#[inline]
pub fn len(&self) -> usize {
self.len
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len == 0
}
#[inline]
pub fn set_len(&mut self, len: usize) {
self.len = len;
}
pub fn as_bytes(&self) -> &[u8] {
if self.ptr.is_null() || self.len == 0 {
&[]
} else {
unsafe { std::slice::from_raw_parts(self.ptr as *const u8, self.len) }
}
}
pub fn as_bytes_mut(&mut self) -> &mut [u8] {
if self.ptr.is_null() || self.len == 0 {
&mut []
} else {
unsafe { std::slice::from_raw_parts_mut(self.ptr as *mut u8, self.len) }
}
}
pub fn set_bytes(&mut self, bytes: &[u8]) -> AtmiResult<()> {
let info = self.tptypes()?;
if bytes.len() > info.size {
self.tprealloc(bytes.len())?;
}
if !bytes.is_empty() {
unsafe {
std::ptr::copy_nonoverlapping(bytes.as_ptr(), self.ptr as *mut u8, bytes.len());
}
}
self.len = bytes.len();
Ok(())
}
pub fn tptypes(&self) -> AtmiResult<TpTypeInfo> {
let mut type_buf = [0i8; raw::XATMI_TYPE_LEN as usize];
let mut subtype_buf = [0i8; raw::XATMI_SUBTYPE_LEN as usize];
#[cfg(not(feature = "ctx-send"))]
let rc = unsafe {
raw::tptypes(
self.ptr,
type_buf.as_mut_ptr() as *mut c_char,
subtype_buf.as_mut_ptr() as *mut c_char,
)
};
#[cfg(feature = "ctx-send")]
let rc = unsafe {
raw::Otptypes(
self.ctx.c_ctx_ptr(),
self.ptr,
type_buf.as_mut_ptr() as *mut c_char,
subtype_buf.as_mut_ptr() as *mut c_char,
)
};
if rc < 0 {
Err(self.ctx.atmi_last_error())
} else {
let type_name = unsafe { CStr::from_ptr(type_buf.as_ptr() as *const c_char) }
.to_string_lossy()
.into_owned();
let subtype = unsafe { CStr::from_ptr(subtype_buf.as_ptr() as *const c_char) }
.to_string_lossy()
.into_owned();
Ok(TpTypeInfo {
size: rc as usize,
type_name,
subtype,
})
}
}
pub fn tprealloc(&mut self, new_size: usize) -> AtmiResult<()> {
#[cfg(not(feature = "ctx-send"))]
let new_ptr = unsafe { raw::tprealloc(self.ptr as *mut c_char, new_size as c_long) };
#[cfg(feature = "ctx-send")]
let new_ptr =
unsafe { raw::Otprealloc(self.ctx.c_ctx_ptr(), self.ptr, new_size as c_long) };
if new_ptr.is_null() {
Err(self.ctx.atmi_last_error())
} else {
self.ptr = new_ptr;
Ok(())
}
}
}
impl<'ctx> Drop for TypedBuffer<'ctx> {
fn drop(&mut self) {
if self.owned && !self.ptr.is_null() {
#[cfg(not(feature = "ctx-send"))]
unsafe {
raw::tpfree(self.ptr)
}
#[cfg(feature = "ctx-send")]
unsafe {
raw::Otpfree(self.ctx.c_ctx_ptr(), self.ptr)
}
}
}
}