use std::sync::Arc;
use std::sync::atomic::{AtomicU8, AtomicUsize, Ordering};
#[derive(Debug)]
pub struct SharedBytes {
bytes: Box<[AtomicU8]>,
len: AtomicUsize,
}
impl SharedBytes {
pub fn new(len: usize, capacity: usize) -> Arc<SharedBytes> {
Arc::new(SharedBytes {
bytes: (0..capacity.max(len)).map(|_| AtomicU8::new(0)).collect(),
len: AtomicUsize::new(len),
})
}
pub fn len(&self) -> usize {
self.len.load(Ordering::SeqCst)
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn capacity(&self) -> usize {
self.bytes.len()
}
pub fn grow(&self, len: usize) -> bool {
let current = self.len();
len >= current
&& len <= self.capacity()
&& self
.len
.compare_exchange(current, len, Ordering::SeqCst, Ordering::SeqCst)
.is_ok()
}
pub fn byte(&self, index: usize) -> &AtomicU8 {
&self.bytes[index]
}
}
#[derive(Debug)]
pub enum Store {
Owned(Vec<u8>),
Shared(Arc<SharedBytes>),
}
#[derive(Debug)]
pub struct Buffer {
pub store: Store,
pub detached: bool,
pub max: Option<usize>,
}
impl Buffer {
pub fn owned(len: usize, max: Option<usize>) -> Buffer {
Buffer {
store: Store::Owned(vec![0; len]),
detached: false,
max,
}
}
pub fn shared(len: usize, max: Option<usize>) -> Buffer {
Buffer {
store: Store::Shared(SharedBytes::new(len, max.unwrap_or(len))),
detached: false,
max,
}
}
pub fn is_shared(&self) -> bool {
matches!(self.store, Store::Shared(_))
}
pub fn len(&self) -> usize {
match &self.store {
Store::Owned(bytes) => bytes.len(),
Store::Shared(shared) => shared.len(),
}
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn owned_bytes(&self) -> usize {
match &self.store {
Store::Owned(bytes) => bytes.capacity(),
Store::Shared(shared) => shared.capacity(),
}
}
#[inline]
pub fn read(&self, offset: usize, out: &mut [u8]) {
if out.is_empty() {
return;
}
match &self.store {
Store::Owned(bytes) => out.copy_from_slice(&bytes[offset..offset + out.len()]),
Store::Shared(shared) => {
for (index, byte) in out.iter_mut().enumerate() {
*byte = shared.byte(offset + index).load(Ordering::Relaxed);
}
}
}
}
#[inline]
pub fn write(&mut self, offset: usize, data: &[u8]) {
if data.is_empty() {
return;
}
match &mut self.store {
Store::Owned(bytes) => bytes[offset..offset + data.len()].copy_from_slice(data),
Store::Shared(shared) => {
for (index, byte) in data.iter().enumerate() {
shared.byte(offset + index).store(*byte, Ordering::Relaxed);
}
}
}
}
pub fn copy_within(&mut self, from: usize, to: usize, len: usize) {
if len == 0 {
return;
}
match &mut self.store {
Store::Owned(bytes) => bytes.copy_within(from..from + len, to),
Store::Shared(_) => {
let mut temporary = vec![0; len];
self.read(from, &mut temporary);
self.write(to, &temporary);
}
}
}
pub fn owned_slice(&self) -> Option<&[u8]> {
match &self.store {
Store::Owned(bytes) => Some(bytes),
Store::Shared(_) => None,
}
}
pub fn owned_slice_mut(&mut self) -> Option<&mut Vec<u8>> {
match &mut self.store {
Store::Owned(bytes) => Some(bytes),
Store::Shared(_) => None,
}
}
pub fn to_vec(&self, offset: usize, len: usize) -> Vec<u8> {
let mut out = vec![0; len];
self.read(offset, &mut out);
out
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum ElementKind {
Int8,
Uint8,
Uint8Clamped,
Int16,
Uint16,
Int32,
Uint32,
Float16,
Float32,
Float64,
BigInt64,
BigUint64,
}
impl ElementKind {
pub const ALL: [ElementKind; 12] = [
ElementKind::Int8,
ElementKind::Uint8,
ElementKind::Uint8Clamped,
ElementKind::Int16,
ElementKind::Uint16,
ElementKind::Int32,
ElementKind::Uint32,
ElementKind::Float16,
ElementKind::Float32,
ElementKind::Float64,
ElementKind::BigInt64,
ElementKind::BigUint64,
];
#[inline]
pub const fn size(self) -> usize {
match self {
ElementKind::Int8 | ElementKind::Uint8 | ElementKind::Uint8Clamped => 1,
ElementKind::Int16 | ElementKind::Uint16 | ElementKind::Float16 => 2,
ElementKind::Int32 | ElementKind::Uint32 | ElementKind::Float32 => 4,
ElementKind::Float64 | ElementKind::BigInt64 | ElementKind::BigUint64 => 8,
}
}
#[inline]
pub const fn is_bigint(self) -> bool {
matches!(self, ElementKind::BigInt64 | ElementKind::BigUint64)
}
pub const fn name(self) -> &'static str {
match self {
ElementKind::Int8 => "Int8Array",
ElementKind::Uint8 => "Uint8Array",
ElementKind::Uint8Clamped => "Uint8ClampedArray",
ElementKind::Int16 => "Int16Array",
ElementKind::Uint16 => "Uint16Array",
ElementKind::Int32 => "Int32Array",
ElementKind::Uint32 => "Uint32Array",
ElementKind::Float16 => "Float16Array",
ElementKind::Float32 => "Float32Array",
ElementKind::Float64 => "Float64Array",
ElementKind::BigInt64 => "BigInt64Array",
ElementKind::BigUint64 => "BigUint64Array",
}
}
pub fn from_index(index: u8) -> ElementKind {
ElementKind::ALL[index as usize]
}
}
#[derive(Clone, Copy, Debug)]
pub struct View {
pub kind: ElementKind,
pub byte_offset: usize,
pub length: Option<usize>,
}
impl View {
#[inline]
pub fn current_length(
&self,
buffer_len: usize,
detached: bool,
element: usize,
) -> Option<usize> {
if detached || self.byte_offset > buffer_len {
return None;
}
match self.length {
Some(length) => (self.byte_offset + length * element <= buffer_len).then_some(length),
None => Some((buffer_len - self.byte_offset) / element),
}
}
}
pub const MAX_BUFFER_LENGTH: usize = i32::MAX as usize;
impl super::Heap {
pub(crate) fn charge(
&mut self,
extra: usize,
keep: &[crate::value::Value],
) -> Result<(), super::HeapError> {
self.safepoint(
&super::Need {
extra,
..Default::default()
},
keep,
)
}
}