use std::{marker::PhantomData, slice};
use crate::{
context::{internal::Env, Context},
handle::{internal::TransparentNoCopyWrapper, Handle, Managed},
object::Object,
result::{JsResult, Throw},
sys::{self, raw, TypedArrayType},
types_impl::{
buffer::{
lock::{Ledger, Lock},
private::{self, JsTypedArrayInner},
BorrowError, Ref, RefMut, Region, TypedArray,
},
private::ValueInternal,
Value,
},
};
#[cfg(feature = "doc-comment")]
use doc_comment::doc_comment;
#[cfg(not(feature = "doc-comment"))]
macro_rules! doc_comment {
{$comment:expr, $decl:item} => { $decl };
}
#[derive(Debug)]
#[repr(transparent)]
pub struct JsBuffer(raw::Local);
impl JsBuffer {
pub fn new<'a, C: Context<'a>>(cx: &mut C, len: usize) -> JsResult<'a, Self> {
let result = unsafe { sys::buffer::new(cx.env().to_raw(), len) };
if let Ok(buf) = result {
Ok(Handle::new_internal(Self(buf)))
} else {
Err(Throw::new())
}
}
pub fn from_slice<'cx, C>(cx: &mut C, slice: &[u8]) -> JsResult<'cx, Self>
where
C: Context<'cx>,
{
<JsBuffer as TypedArray>::from_slice(cx, slice)
}
pub unsafe fn uninitialized<'a, C: Context<'a>>(cx: &mut C, len: usize) -> JsResult<'a, Self> {
let result = sys::buffer::uninitialized(cx.env().to_raw(), len);
if let Ok((buf, _)) = result {
Ok(Handle::new_internal(Self(buf)))
} else {
Err(Throw::new())
}
}
#[cfg(feature = "external-buffers")]
#[cfg_attr(docsrs, doc(cfg(feature = "external-buffers")))]
pub fn external<'a, C, T>(cx: &mut C, data: T) -> Handle<'a, Self>
where
C: Context<'a>,
T: AsMut<[u8]> + Send + 'static,
{
let env = cx.env().to_raw();
let value = unsafe { sys::buffer::new_external(env, data) };
Handle::new_internal(Self(value))
}
}
unsafe impl TransparentNoCopyWrapper for JsBuffer {
type Inner = raw::Local;
fn into_inner(self) -> Self::Inner {
self.0
}
}
impl Managed for JsBuffer {
fn to_raw(&self) -> raw::Local {
self.0
}
fn from_raw(_env: Env, h: raw::Local) -> Self {
Self(h)
}
}
impl ValueInternal for JsBuffer {
fn name() -> String {
"Buffer".to_string()
}
fn is_typeof<Other: Value>(env: Env, other: &Other) -> bool {
unsafe { sys::tag::is_buffer(env.to_raw(), other.to_raw()) }
}
}
impl Value for JsBuffer {}
impl Object for JsBuffer {}
impl private::Sealed for JsBuffer {}
impl TypedArray for JsBuffer {
type Item = u8;
fn as_slice<'cx, 'a, C>(&self, cx: &'a C) -> &'a [Self::Item]
where
C: Context<'cx>,
{
unsafe { sys::buffer::as_mut_slice(cx.env().to_raw(), self.to_raw()) }
}
fn as_mut_slice<'cx, 'a, C>(&mut self, cx: &'a mut C) -> &'a mut [Self::Item]
where
C: Context<'cx>,
{
unsafe { sys::buffer::as_mut_slice(cx.env().to_raw(), self.to_raw()) }
}
fn try_borrow<'cx, 'a, C>(&self, lock: &'a Lock<C>) -> Result<Ref<'a, Self::Item>, BorrowError>
where
C: Context<'cx>,
{
Ledger::try_borrow(&lock.ledger, unsafe {
sys::buffer::as_mut_slice(lock.cx.env().to_raw(), self.to_raw())
})
}
fn try_borrow_mut<'cx, 'a, C>(
&mut self,
lock: &'a Lock<C>,
) -> Result<RefMut<'a, Self::Item>, BorrowError>
where
C: Context<'cx>,
{
Ledger::try_borrow_mut(&lock.ledger, unsafe {
sys::buffer::as_mut_slice(lock.cx.env().to_raw(), self.to_raw())
})
}
fn size<'cx, C: Context<'cx>>(&self, cx: &mut C) -> usize {
unsafe { sys::buffer::size(cx.env().to_raw(), self.to_raw()) }
}
fn from_slice<'cx, C>(cx: &mut C, slice: &[u8]) -> JsResult<'cx, Self>
where
C: Context<'cx>,
{
let mut buffer = cx.buffer(slice.len())?;
let target = buffer.as_mut_slice(cx);
target.copy_from_slice(slice);
Ok(buffer)
}
}
#[derive(Debug)]
#[repr(transparent)]
pub struct JsArrayBuffer(raw::Local);
impl JsArrayBuffer {
pub fn new<'a, C: Context<'a>>(cx: &mut C, len: usize) -> JsResult<'a, Self> {
let result = unsafe { sys::arraybuffer::new(cx.env().to_raw(), len) };
if let Ok(buf) = result {
Ok(Handle::new_internal(Self(buf)))
} else {
Err(Throw::new())
}
}
pub fn from_slice<'cx, C>(cx: &mut C, slice: &[u8]) -> JsResult<'cx, Self>
where
C: Context<'cx>,
{
<JsArrayBuffer as TypedArray>::from_slice(cx, slice)
}
#[cfg(feature = "external-buffers")]
#[cfg_attr(docsrs, doc(cfg(feature = "external-buffers")))]
pub fn external<'a, C, T>(cx: &mut C, data: T) -> Handle<'a, Self>
where
C: Context<'a>,
T: AsMut<[u8]> + Send + 'static,
{
let env = cx.env().to_raw();
let value = unsafe { sys::arraybuffer::new_external(env, data) };
Handle::new_internal(Self(value))
}
pub fn region<'cx, T: Binary>(
buffer: &Handle<'cx, JsArrayBuffer>,
offset: usize,
len: usize,
) -> Region<'cx, T> {
buffer.region(offset, len)
}
}
impl<'cx> Handle<'cx, JsArrayBuffer> {
pub fn region<T: Binary>(&self, offset: usize, len: usize) -> Region<'cx, T> {
Region {
buffer: *self,
offset,
len,
phantom: PhantomData,
}
}
}
unsafe impl TransparentNoCopyWrapper for JsArrayBuffer {
type Inner = raw::Local;
fn into_inner(self) -> Self::Inner {
self.0
}
}
impl Managed for JsArrayBuffer {
fn to_raw(&self) -> raw::Local {
self.0
}
fn from_raw(_env: Env, h: raw::Local) -> Self {
Self(h)
}
}
impl ValueInternal for JsArrayBuffer {
fn name() -> String {
"JsArrayBuffer".to_string()
}
fn is_typeof<Other: Value>(env: Env, other: &Other) -> bool {
unsafe { sys::tag::is_arraybuffer(env.to_raw(), other.to_raw()) }
}
}
impl Value for JsArrayBuffer {}
impl Object for JsArrayBuffer {}
impl private::Sealed for JsArrayBuffer {}
impl TypedArray for JsArrayBuffer {
type Item = u8;
fn as_slice<'cx, 'a, C>(&self, cx: &'a C) -> &'a [Self::Item]
where
C: Context<'cx>,
{
unsafe { sys::arraybuffer::as_mut_slice(cx.env().to_raw(), self.to_raw()) }
}
fn as_mut_slice<'cx, 'a, C>(&mut self, cx: &'a mut C) -> &'a mut [Self::Item]
where
C: Context<'cx>,
{
unsafe { sys::arraybuffer::as_mut_slice(cx.env().to_raw(), self.to_raw()) }
}
fn try_borrow<'cx, 'a, C>(&self, lock: &'a Lock<C>) -> Result<Ref<'a, Self::Item>, BorrowError>
where
C: Context<'cx>,
{
Ledger::try_borrow(&lock.ledger, unsafe {
sys::arraybuffer::as_mut_slice(lock.cx.env().to_raw(), self.to_raw())
})
}
fn try_borrow_mut<'cx, 'a, C>(
&mut self,
lock: &'a Lock<C>,
) -> Result<RefMut<'a, Self::Item>, BorrowError>
where
C: Context<'cx>,
{
Ledger::try_borrow_mut(&lock.ledger, unsafe {
sys::arraybuffer::as_mut_slice(lock.cx.env().to_raw(), self.to_raw())
})
}
fn size<'cx, C: Context<'cx>>(&self, cx: &mut C) -> usize {
unsafe { sys::arraybuffer::size(cx.env().to_raw(), self.to_raw()) }
}
fn from_slice<'cx, C>(cx: &mut C, slice: &[u8]) -> JsResult<'cx, Self>
where
C: Context<'cx>,
{
let len = slice.len();
let mut buffer = JsArrayBuffer::new(cx, len)?;
let target = buffer.as_mut_slice(cx);
target.copy_from_slice(slice);
Ok(buffer)
}
}
pub trait Binary: private::Sealed + Copy {
const TYPE_TAG: TypedArrayType;
}
#[derive(Debug)]
#[repr(transparent)]
pub struct JsTypedArray<T: Binary>(JsTypedArrayInner<T>);
impl<T: Binary> private::Sealed for JsTypedArray<T> {}
unsafe impl<T: Binary> TransparentNoCopyWrapper for JsTypedArray<T> {
type Inner = JsTypedArrayInner<T>;
fn into_inner(self) -> Self::Inner {
self.0
}
}
impl<T: Binary> Managed for JsTypedArray<T> {
fn to_raw(&self) -> raw::Local {
self.0.local
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
fn from_raw(env: Env, local: raw::Local) -> Self {
let info = unsafe { sys::typedarray::info(env.to_raw(), local) };
Self(JsTypedArrayInner {
local,
buffer: info.buf,
_type: PhantomData,
})
}
}
impl<T> TypedArray for JsTypedArray<T>
where
T: Binary,
Self: Value,
{
type Item = T;
fn as_slice<'cx, 'a, C>(&self, cx: &'a C) -> &'a [Self::Item]
where
C: Context<'cx>,
{
unsafe {
let env = cx.env().to_raw();
let value = self.to_raw();
let info = sys::typedarray::info(env, value);
slice::from_raw_parts(info.data.cast(), info.length)
}
}
fn as_mut_slice<'cx, 'a, C>(&mut self, cx: &'a mut C) -> &'a mut [Self::Item]
where
C: Context<'cx>,
{
unsafe {
let env = cx.env().to_raw();
let value = self.to_raw();
let info = sys::typedarray::info(env, value);
slice::from_raw_parts_mut(info.data.cast(), info.length)
}
}
fn try_borrow<'cx, 'b, C>(
&self,
lock: &'b Lock<'b, C>,
) -> Result<Ref<'b, Self::Item>, BorrowError>
where
C: Context<'cx>,
{
unsafe {
let env = lock.cx.env().to_raw();
let value = self.to_raw();
let info = sys::typedarray::info(env, value);
Ledger::try_borrow(
&lock.ledger,
slice::from_raw_parts(info.data.cast(), info.length),
)
}
}
fn try_borrow_mut<'cx, 'a, C>(
&mut self,
lock: &'a Lock<'a, C>,
) -> Result<RefMut<'a, Self::Item>, BorrowError>
where
C: Context<'cx>,
{
unsafe {
let env = lock.cx.env().to_raw();
let value = self.to_raw();
let info = sys::typedarray::info(env, value);
Ledger::try_borrow_mut(
&lock.ledger,
slice::from_raw_parts_mut(info.data.cast(), info.length),
)
}
}
fn size<'cx, C: Context<'cx>>(&self, cx: &mut C) -> usize {
self.len(cx) * std::mem::size_of::<Self::Item>()
}
fn from_slice<'cx, C>(cx: &mut C, slice: &[T]) -> JsResult<'cx, Self>
where
C: Context<'cx>,
{
let elt_size = std::mem::size_of::<T>();
let size = slice.len() * elt_size;
let buffer = cx.array_buffer(size)?;
let mut array = Self::from_buffer(cx, buffer)?;
let target = array.as_mut_slice(cx);
target.copy_from_slice(slice);
Ok(array)
}
}
impl<T: Binary> JsTypedArray<T>
where
JsTypedArray<T>: Value,
{
pub fn from_slice<'cx, C>(cx: &mut C, slice: &[T]) -> JsResult<'cx, Self>
where
C: Context<'cx>,
{
<JsTypedArray<T> as TypedArray>::from_slice(cx, slice)
}
}
impl<T: Binary> JsTypedArray<T> {
pub fn from_buffer<'cx, 'b: 'cx, C>(
cx: &mut C,
buffer: Handle<'b, JsArrayBuffer>,
) -> JsResult<'cx, Self>
where
C: Context<'cx>,
{
let size = buffer.size(cx);
let elt_size = std::mem::size_of::<T>();
let len = size / elt_size;
if (len * elt_size) != size {
return cx.throw_range_error(format!(
"byte length of typed array should be a multiple of {}",
elt_size
));
}
Self::from_region(cx, &buffer.region(0, len))
}
pub fn from_region<'c, 'r, C>(cx: &mut C, region: &Region<'r, T>) -> JsResult<'c, Self>
where
C: Context<'c>,
{
let &Region {
buffer,
offset,
len,
..
} = region;
let arr = (unsafe {
sys::typedarray::new(cx.env().to_raw(), T::TYPE_TAG, buffer.to_raw(), offset, len)
})
.map_err(|_| Throw::new())?;
Ok(Handle::new_internal(Self(JsTypedArrayInner {
local: arr,
buffer: buffer.to_raw(),
_type: PhantomData,
})))
}
pub fn region<'cx, C>(&self, cx: &mut C) -> Region<'cx, T>
where
C: Context<'cx>,
{
let env = cx.env();
let info = unsafe { sys::typedarray::info(env.to_raw(), self.to_raw()) };
Region {
buffer: Handle::new_internal(JsArrayBuffer::from_raw(cx.env(), info.buf)),
offset: info.offset,
len: info.length,
phantom: PhantomData,
}
}
pub fn new<'cx, C>(cx: &mut C, len: usize) -> JsResult<'cx, Self>
where
C: Context<'cx>,
{
let buffer = cx.array_buffer(len * std::mem::size_of::<T>())?;
Self::from_region(cx, &buffer.region(0, len))
}
pub fn buffer<'cx, C>(&self, cx: &mut C) -> Handle<'cx, JsArrayBuffer>
where
C: Context<'cx>,
{
Handle::new_internal(JsArrayBuffer::from_raw(cx.env(), self.0.buffer))
}
pub fn offset<'cx, C>(&self, cx: &mut C) -> usize
where
C: Context<'cx>,
{
let info = unsafe { sys::typedarray::info(cx.env().to_raw(), self.to_raw()) };
info.offset
}
#[allow(clippy::len_without_is_empty)]
pub fn len<'cx, C>(&self, cx: &mut C) -> usize
where
C: Context<'cx>,
{
let info = unsafe { sys::typedarray::info(cx.env().to_raw(), self.to_raw()) };
info.length
}
}
macro_rules! impl_typed_array {
($typ:ident, $etyp:ty, $($pattern:pat)|+, $tag:ident, $alias:ident, $two:expr$(,)?) => {
impl private::Sealed for $etyp {}
impl Binary for $etyp {
const TYPE_TAG: TypedArrayType = TypedArrayType::$tag;
}
impl Value for JsTypedArray<$etyp> {}
impl Object for JsTypedArray<$etyp> {}
impl ValueInternal for JsTypedArray<$etyp> {
fn name() -> String {
stringify!($typ).to_string()
}
fn is_typeof<Other: Value>(env: Env, other: &Other) -> bool {
let env = env.to_raw();
let other = other.to_raw();
if unsafe { !sys::tag::is_typedarray(env, other) } {
return false;
}
let info = unsafe { sys::typedarray::info(env, other) };
matches!(info.typ, $($pattern)|+)
}
}
doc_comment! {
concat!(
"The standard JS [`",
stringify!($typ),
"`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/",
stringify!($typ),
") type.
# Example
```
# use neon::prelude::*;
use neon::types::buffer::TypedArray;
fn double(mut cx: FunctionContext) -> JsResult<JsUndefined> {
let mut array: Handle<",
stringify!($alias),
"> = cx.argument(0)?;
for elem in array.as_mut_slice(&mut cx).iter_mut() {
*elem *= ",
stringify!($two),
";
}
Ok(cx.undefined())
}
```",
),
pub type $alias = JsTypedArray<$etyp>;
}
};
}
impl_typed_array!(Int8Array, i8, TypedArrayType::I8, I8, JsInt8Array, 2);
impl_typed_array!(
Uint8Array,
u8,
TypedArrayType::U8 | TypedArrayType::U8Clamped,
U8,
JsUint8Array,
2,
);
impl_typed_array!(Int16Array, i16, TypedArrayType::I16, I16, JsInt16Array, 2);
impl_typed_array!(Uint16Array, u16, TypedArrayType::U16, U16, JsUint16Array, 2);
impl_typed_array!(Int32Array, i32, TypedArrayType::I32, I32, JsInt32Array, 2);
impl_typed_array!(Uint32Array, u32, TypedArrayType::U32, U32, JsUint32Array, 2);
impl_typed_array!(
Float32Array,
f32,
TypedArrayType::F32,
F32,
JsFloat32Array,
2.0,
);
impl_typed_array!(
Float64Array,
f64,
TypedArrayType::F64,
F64,
JsFloat64Array,
2.0,
);
impl_typed_array!(
BigInt64Array,
i64,
TypedArrayType::I64,
I64,
JsBigInt64Array,
2,
);
impl_typed_array!(
BigUint64Array,
u64,
TypedArrayType::U64,
U64,
JsBigUint64Array,
2,
);