use std::ptr;
use crate::{decorate_for_target, Context};
static mut LOG_RET_AREA: [usize; 5] = [0; 5];
const CAPACITY: usize = 1001;
#[derive(Debug)]
pub(crate) struct Logs {
buffer: [u8; CAPACITY],
offset: usize,
len: usize,
}
impl Default for Logs {
fn default() -> Self {
Self {
buffer: [0; CAPACITY],
offset: 0,
len: 0,
}
}
}
impl Logs {
fn append(&mut self, mut len: usize) -> (usize, *const u8, usize, *const u8, usize) {
let mut source_offset = 0;
let dst_offset1 = unsafe { self.buffer.as_ptr().add(self.offset) };
let len1;
let mut dst_offset2 = ptr::null();
let mut len2 = 0;
if len > CAPACITY {
source_offset = len - CAPACITY;
len = CAPACITY;
}
let space_to_end = CAPACITY - self.offset;
if len <= space_to_end {
len1 = len;
self.len = (self.len + len).min(CAPACITY);
} else {
len1 = space_to_end;
dst_offset2 = self.buffer.as_ptr();
len2 = len - space_to_end;
self.len = CAPACITY;
}
self.offset = (self.offset + len) % CAPACITY;
(source_offset, dst_offset1, len1, dst_offset2, len2)
}
#[cfg(target_family = "wasm")]
pub(crate) fn read_ptrs(&self) -> (*const u8, usize, *const u8, usize) {
let read_offset = if self.len < CAPACITY { 0 } else { self.offset };
if read_offset == 0 {
(self.buffer.as_ptr(), self.len, ptr::null(), 0)
} else {
let data_to_end = CAPACITY - read_offset;
(
unsafe { self.buffer.as_ptr().add(self.offset) },
data_to_end,
self.buffer.as_ptr(),
self.len - data_to_end,
)
}
}
}
impl Context {
fn allocate_log(&mut self, len: usize) -> (usize, *const u8, usize, *const u8, usize) {
self.logs.append(len)
}
}
decorate_for_target! {
fn shopify_function_log_new_utf8_str(len: usize) -> *const usize {
Context::with_mut(|context| {
let (src_offset, ptr1, len1, ptr2, len2) = context.allocate_log(len);
#[allow(static_mut_refs)] unsafe {
LOG_RET_AREA[0] = src_offset;
LOG_RET_AREA[1] = ptr1 as usize;
LOG_RET_AREA[2] = len1;
LOG_RET_AREA[3] = ptr2 as usize;
LOG_RET_AREA[4] = len2;
LOG_RET_AREA.as_ptr()
}
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_append_fits_in_buffer() {
let mut logs = Logs::default();
let (source_offset, ptr1, len1, ptr2, len2) = logs.append(100);
assert_eq!(source_offset, 0);
assert_eq!(logs.len, 100);
assert_eq!(logs.offset, 100);
assert_eq!(ptr1, logs.buffer.as_ptr());
assert_eq!(len1, 100);
assert_eq!(len2, 0);
assert!(ptr2.is_null());
}
#[test]
fn test_append_exceeds_capacity() {
let mut logs = Logs::default();
let large_len = CAPACITY + 100;
let (source_offset, ptr1, len1, ptr2, len2) = logs.append(large_len);
assert_eq!(source_offset, 100);
assert_eq!(logs.len, CAPACITY);
assert_eq!(logs.offset, 0);
assert_eq!(ptr1, logs.buffer.as_ptr());
assert_eq!(len1, CAPACITY);
assert_eq!(len2, 0);
assert!(ptr2.is_null());
}
#[test]
fn test_append_zero_length() {
let mut logs = Logs::default();
let (source_offset, ptr1, len1, ptr2, len2) = logs.append(0);
assert_eq!(source_offset, 0);
assert_eq!(logs.len, 0);
assert_eq!(logs.offset, 0);
assert_eq!(ptr1, logs.buffer.as_ptr());
assert_eq!(len1, 0);
assert_eq!(len2, 0);
assert!(ptr2.is_null());
}
#[test]
fn test_append_exact_capacity() {
let mut logs = Logs::default();
let (source_offset, ptr1, len1, ptr2, len2) = logs.append(CAPACITY);
assert_eq!(source_offset, 0);
assert_eq!(logs.len, CAPACITY);
assert_eq!(logs.offset, 0);
assert_eq!(ptr1, logs.buffer.as_ptr());
assert_eq!(len1, CAPACITY);
assert_eq!(len2, 0);
assert!(ptr2.is_null());
}
#[test]
fn test_append_multiple_operations() {
let mut logs = Logs::default();
let (source_offset, ptr1, len1, ptr2, len2) = logs.append(300);
assert_eq!(source_offset, 0);
assert_eq!(logs.len, 300);
assert_eq!(logs.offset, 300);
assert_eq!(ptr1, logs.buffer.as_ptr());
assert_eq!(len1, 300);
assert_eq!(ptr2, ptr::null());
assert_eq!(len2, 0);
let (source_offset, ptr1, len1, ptr2, len2) = logs.append(200);
assert_eq!(source_offset, 0);
assert_eq!(logs.len, 500);
assert_eq!(logs.offset, 500);
assert_eq!(ptr1, unsafe { logs.buffer.as_ptr().add(300) });
assert_eq!(len1, 200);
assert_eq!(ptr2, ptr::null());
assert_eq!(len2, 0);
let (source_offset, ptr1, len1, ptr2, len2) = logs.append(600); assert_eq!(source_offset, 0);
assert_eq!(logs.len, CAPACITY);
assert_eq!(logs.offset, 99); assert_eq!(ptr1, unsafe { logs.buffer.as_ptr().add(500) });
assert_eq!(len1, 501);
assert_eq!(ptr2, logs.buffer.as_ptr());
assert_eq!(len2, 99);
let (source_offset, ptr1, len1, ptr2, len2) = logs.append(100); assert_eq!(source_offset, 0);
assert_eq!(logs.len, CAPACITY);
assert_eq!(logs.offset, 199); assert_eq!(ptr1, unsafe { logs.buffer.as_ptr().add(99) });
assert_eq!(len1, 100);
assert_eq!(ptr2, ptr::null());
assert_eq!(len2, 0);
}
}