use std::fmt;
use super::{AmxCell, Buffer, UnsizedBuffer};
use crate::amx::Amx;
use crate::error::AmxResult;
#[cfg(feature = "encoding")]
use crate::encoding;
const MAX_UNPACKED: i32 = 0x00FF_FFFF;
pub struct AmxString<'amx> {
inner: Buffer<'amx>,
len: usize,
}
impl<'amx> AmxString<'amx> {
pub unsafe fn new(mut buffer: Buffer<'amx>, bytes: &[u8]) -> AmxString<'amx> {
for (idx, byte) in bytes.iter().enumerate() {
buffer[idx] = i32::from(*byte);
}
buffer[bytes.len()] = 0;
AmxString {
len: buffer.len(),
inner: buffer,
}
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut vec = Vec::with_capacity(self.len);
if self.inner[0] > MAX_UNPACKED {
unsafe {
std::ptr::copy(
self.inner.as_ptr() as *const u8,
vec.as_mut_ptr(),
vec.len(),
);
}
} else {
for item in self.inner.iter().take(self.len) {
vec.push(*item as u8);
}
}
vec
}
pub fn to_string(&self) -> String {
#[cfg(feature = "encoding")]
return encoding::get().decode(&self.to_bytes()).0.into_owned();
#[cfg(not(feature = "encoding"))]
return unsafe { String::from_utf8_unchecked(self.to_bytes()) };
}
pub fn len(&self) -> usize {
self.len
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn bytes_len(&self) -> usize {
self.inner.len()
}
}
impl<'amx> AmxCell<'amx> for AmxString<'amx> {
fn from_raw(amx: &'amx Amx, cell: i32) -> AmxResult<AmxString<'amx>> {
let buffer = UnsizedBuffer::from_raw(amx, cell)?;
let ptr = buffer.as_ptr();
let packed = unsafe { ptr.read() > MAX_UNPACKED };
let (str_len, buf_len) = {
if packed {
let len = strlen(ptr as *const i8, 0);
let buf_len = len / 4 + 1; (len, buf_len)
} else {
let len = strlen(ptr, 0);
(len, len + 1)
}
};
Ok(AmxString {
inner: buffer.into_sized_buffer(buf_len),
len: str_len,
})
}
fn as_cell(&self) -> i32 {
self.inner.as_cell()
}
}
fn strlen<T: PartialEq>(mut string: *const T, zerochar: T) -> usize {
let mut length = 0;
unsafe {
while string.read() != zerochar {
string = string.offset(1);
length += 1;
}
}
length
}
impl fmt::Display for AmxString<'_> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "{}", self.to_string())
}
}
pub fn put_in_buffer(buffer: &mut Buffer, string: &str) -> AmxResult<()> {
#[cfg(feature = "encoding")]
let bytes = encoding::get().encode(string).0;
#[cfg(not(feature = "encoding"))]
let bytes = std::borrow::Cow::from(string.as_bytes());
let bytes = bytes.as_ref();
if bytes.len() >= buffer.len() {
return Err(crate::error::AmxError::General);
}
for (idx, byte) in bytes.iter().enumerate() {
buffer[idx] = i32::from(*byte);
}
buffer[bytes.len()] = 0;
Ok(())
}