use std::ptr;

#[no_mangle]
pub unsafe fn malloc(len: usize) -> *mut u8 {
    let align = std::mem::align_of::<usize>();
    let layout = std::alloc::Layout::from_size_align_unchecked(len + 4, align);
    let addr = std::alloc::alloc(layout);
    let len_in_bytes = (len as u32).to_be_bytes();
    write_bytes(addr, 0, 4, &len_in_bytes);
    addr
}

#[no_mangle]
pub unsafe fn free(ptr: *mut u8) {
    let len = read_u32(ptr);
    let align = std::mem::align_of::<usize>();
    let layout = std::alloc::Layout::from_size_align_unchecked(4 + len as usize, align);
    std::alloc::dealloc(ptr, layout)
}

pub(crate) fn read_string(ptr: *const u8) -> String {
    let len = read_u32(ptr);
    let buffer = read_bytes(ptr, 4, len as usize);
    unsafe { String::from_utf8_unchecked(buffer) }
}

pub(crate) unsafe fn malloc_and_write_string(str: &str) -> (*mut u8, usize) {
    let bytes = str.as_bytes();
    let bytes_len = bytes.len();
    let ptr = malloc(bytes_len);
    write_bytes(ptr, 4, bytes_len, bytes);
    (ptr, bytes_len)
}

pub(crate) fn read_u32(ptr: *const u8) -> u32 {
    let vec_u32 = read_bytes(ptr, 0, 4);
    let mut bytes_u32 = [0_u8; 4];
    bytes_u32[0] = vec_u32[0];
    bytes_u32[1] = vec_u32[1];
    bytes_u32[2] = vec_u32[2];
    bytes_u32[3] = vec_u32[3];
    u32::from_be_bytes(bytes_u32)
}

pub(crate) fn read_bytes(ptr: *const u8, offset: isize, len: usize) -> Vec<u8> {
    let mut buffer = Vec::with_capacity(len);
    for i in 0..len {
        buffer.push(unsafe { ptr::read(ptr.offset(offset + i as isize)) });
    }
    buffer
}

pub(crate) fn write_bytes(ptr: *mut u8, offset: isize, len: usize, bytes: &[u8]) {
    let bytes_len = bytes.len();
    assert!(bytes_len <= len);
    for i in 0..bytes_len {
        unsafe { ptr::write(ptr.offset(offset + i as isize), bytes[i]) };
    }
}
