use std::slice;
use std::vec::Vec;
use std::ptr;
use std::alloc::{self, Layout};
use std::mem;
use JsValue;
externs! {
#[link(wasm_import_module = "__wbindgen_anyref_xform__")]
extern "C" {
fn __wbindgen_anyref_table_grow(delta: usize) -> i32;
fn __wbindgen_anyref_table_set_null(idx: usize) -> ();
}
}
pub struct Slab {
data: Vec<usize>,
head: usize,
base: usize,
}
impl Slab {
fn new() -> Slab {
Slab {
data: Vec::new(),
head: 0,
base: 0,
}
}
fn alloc(&mut self) -> usize {
let ret = self.head;
if ret == self.data.len() {
if self.data.len() == self.data.capacity() {
let extra = 128;
let r = unsafe {
__wbindgen_anyref_table_grow(extra)
};
if r == -1 {
internal_error("table grow failure")
}
if self.base == 0 {
self.base = r as usize + (super::JSIDX_RESERVED as usize);
} else if self.base + self.data.len() != r as usize {
internal_error("someone else allocated table entires?")
}
unsafe {
let new_cap = self.data.capacity() + extra;
let size = mem::size_of::<usize>() * new_cap;
let align = mem::align_of::<usize>();
let layout = match Layout::from_size_align(size, align) {
Ok(l) => l,
Err(_) => internal_error("size/align layout failure"),
};
let ptr = alloc::alloc(layout) as *mut usize;
if ptr.is_null() {
internal_error("allocation failure");
}
ptr::copy_nonoverlapping(
self.data.as_ptr(),
ptr,
self.data.len(),
);
let new_vec = Vec::from_raw_parts(
ptr,
self.data.len(),
new_cap,
);
let mut old = mem::replace(&mut self.data, new_vec);
old.set_len(0);
}
}
if self.data.len() >= self.data.capacity() {
internal_error("push should be infallible now")
}
self.data.push(ret + 1);
}
match self.data.get_mut(ret) {
Some(slot) => self.head = *slot,
None => internal_error("ret out of bounds"),
}
ret + self.base
}
fn dealloc(&mut self, slot: usize) {
if slot < self.base {
internal_error("free reserved slot");
}
let slot = slot - self.base;
match self.data.get_mut(slot) {
Some(ptr) => {
*ptr = self.head;
self.head = slot;
}
None => internal_error("slot out of bounds"),
}
}
}
fn internal_error(msg: &str) -> ! {
if cfg!(debug_assertions) {
super::throw_str(msg)
} else {
std::process::abort()
}
}
#[cfg(target_feature = "atomics")]
mod tl {
use std::*; use super::Slab;
use std::cell::Cell;
thread_local!(pub static HEAP_SLAB: Cell<Slab> = Cell::new(Slab::new()));
}
#[cfg(not(target_feature = "atomics"))]
mod tl {
use std::alloc::{self, Layout};
use std::cell::Cell;
use std::ptr;
use super::Slab;
pub struct HeapSlab;
pub static HEAP_SLAB: HeapSlab = HeapSlab;
static mut SLOT: *mut Cell<Slab> = 0 as *mut Cell<Slab>;
impl HeapSlab {
pub fn try_with<R>(&self, f: impl FnOnce(&Cell<Slab>) -> R) -> Result<R, ()> {
unsafe {
if SLOT.is_null() {
let ptr = alloc::alloc(Layout::new::<Cell<Slab>>());
if ptr.is_null() {
super::internal_error("allocation failure");
}
let ptr = ptr as *mut Cell<Slab>;
ptr::write(ptr, Cell::new(Slab::new()));
SLOT = ptr;
}
Ok(f(&*SLOT))
}
}
}
}
#[no_mangle]
pub extern fn __wbindgen_anyref_table_alloc() -> usize {
tl::HEAP_SLAB.try_with(|slot| {
let mut slab = slot.replace(Slab::new());
let ret = slab.alloc();
slot.replace(slab);
ret
}).unwrap_or_else(|_| internal_error("tls access failure"))
}
#[no_mangle]
pub extern fn __wbindgen_anyref_table_dealloc(idx: usize) {
if idx < super::JSIDX_RESERVED as usize {
return
}
unsafe {
__wbindgen_anyref_table_set_null(idx);
}
tl::HEAP_SLAB.try_with(|slot| {
let mut slab = slot.replace(Slab::new());
slab.dealloc(idx);
slot.replace(slab);
}).unwrap_or_else(|_| internal_error("tls access failure"))
}
#[no_mangle]
pub unsafe extern fn __wbindgen_drop_anyref_slice(ptr: *mut JsValue, len: usize) {
for slot in slice::from_raw_parts_mut(ptr, len) {
ptr::drop_in_place(slot);
}
}
#[inline(never)]
pub fn link_intrinsics() {
}