shopify_function_provider/
log.rs1use std::ptr;
2
3use crate::{decorate_for_target, Context};
4
5static mut LOG_RET_AREA: [usize; 5] = [0; 5];
6const CAPACITY: usize = 1001;
8
9#[derive(Debug)]
13pub(crate) struct Logs {
14 buffer: [u8; CAPACITY],
15 offset: usize,
16 len: usize,
17}
18
19impl Default for Logs {
20 fn default() -> Self {
21 Self {
22 buffer: [0; CAPACITY],
23 offset: 0,
24 len: 0,
25 }
26 }
27}
28
29impl Logs {
30 fn append(&mut self, mut len: usize) -> (usize, *const u8, usize, *const u8, usize) {
31 let mut source_offset = 0;
32 let dst_offset1 = unsafe { self.buffer.as_ptr().add(self.offset) };
33 let len1;
34 let mut dst_offset2 = ptr::null();
35 let mut len2 = 0;
36
37 if len > CAPACITY {
39 source_offset = len - CAPACITY;
40 len = CAPACITY;
41 }
42
43 let space_to_end = CAPACITY - self.offset;
44 if len <= space_to_end {
45 len1 = len;
47 self.len = (self.len + len).min(CAPACITY);
48 } else {
49 len1 = space_to_end;
51 dst_offset2 = self.buffer.as_ptr();
52 len2 = len - space_to_end;
53 self.len = CAPACITY;
54 }
55
56 self.offset = (self.offset + len) % CAPACITY;
57
58 (source_offset, dst_offset1, len1, dst_offset2, len2)
59 }
60
61 #[cfg(target_family = "wasm")]
62 pub(crate) fn read_ptrs(&self) -> (*const u8, usize, *const u8, usize) {
63 let read_offset = if self.len < CAPACITY { 0 } else { self.offset };
66
67 if read_offset == 0 {
68 (self.buffer.as_ptr(), self.len, ptr::null(), 0)
69 } else {
70 let data_to_end = CAPACITY - read_offset;
71 (
72 unsafe { self.buffer.as_ptr().add(self.offset) },
73 data_to_end,
74 self.buffer.as_ptr(),
75 self.len - data_to_end,
76 )
77 }
78 }
79}
80
81impl Context {
82 fn allocate_log(&mut self, len: usize) -> (usize, *const u8, usize, *const u8, usize) {
83 self.logs.append(len)
84 }
85}
86
87decorate_for_target! {
88 fn shopify_function_log_new_utf8_str(len: usize) -> *const usize {
89 Context::with_mut(|context| {
90 let (src_offset, ptr1, len1, ptr2, len2) = context.allocate_log(len);
91 #[allow(static_mut_refs)] unsafe {
93 LOG_RET_AREA[0] = src_offset;
94 LOG_RET_AREA[1] = ptr1 as usize;
95 LOG_RET_AREA[2] = len1;
96 LOG_RET_AREA[3] = ptr2 as usize;
97 LOG_RET_AREA[4] = len2;
98 LOG_RET_AREA.as_ptr()
99 }
100 })
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 #[test]
109 fn test_append_fits_in_buffer() {
110 let mut logs = Logs::default();
111 let (source_offset, ptr1, len1, ptr2, len2) = logs.append(100);
112
113 assert_eq!(source_offset, 0);
114 assert_eq!(logs.len, 100);
115 assert_eq!(logs.offset, 100);
116 assert_eq!(ptr1, logs.buffer.as_ptr());
117 assert_eq!(len1, 100);
118 assert_eq!(len2, 0);
119 assert!(ptr2.is_null());
120 }
121
122 #[test]
123 fn test_append_exceeds_capacity() {
124 let mut logs = Logs::default();
125 let large_len = CAPACITY + 100;
126
127 let (source_offset, ptr1, len1, ptr2, len2) = logs.append(large_len);
128
129 assert_eq!(source_offset, 100);
130 assert_eq!(logs.len, CAPACITY);
131 assert_eq!(logs.offset, 0);
132 assert_eq!(ptr1, logs.buffer.as_ptr());
133 assert_eq!(len1, CAPACITY);
134 assert_eq!(len2, 0);
135 assert!(ptr2.is_null());
136 }
137
138 #[test]
139 fn test_append_zero_length() {
140 let mut logs = Logs::default();
141 let (source_offset, ptr1, len1, ptr2, len2) = logs.append(0);
142
143 assert_eq!(source_offset, 0);
144 assert_eq!(logs.len, 0);
145 assert_eq!(logs.offset, 0);
146 assert_eq!(ptr1, logs.buffer.as_ptr());
147 assert_eq!(len1, 0);
148 assert_eq!(len2, 0);
149 assert!(ptr2.is_null());
150 }
151
152 #[test]
153 fn test_append_exact_capacity() {
154 let mut logs = Logs::default();
155 let (source_offset, ptr1, len1, ptr2, len2) = logs.append(CAPACITY);
156
157 assert_eq!(source_offset, 0);
158 assert_eq!(logs.len, CAPACITY);
159 assert_eq!(logs.offset, 0);
160 assert_eq!(ptr1, logs.buffer.as_ptr());
161 assert_eq!(len1, CAPACITY);
162 assert_eq!(len2, 0);
163 assert!(ptr2.is_null());
164 }
165
166 #[test]
167 fn test_append_multiple_operations() {
168 let mut logs = Logs::default();
169
170 let (source_offset, ptr1, len1, ptr2, len2) = logs.append(300);
171 assert_eq!(source_offset, 0);
172 assert_eq!(logs.len, 300);
173 assert_eq!(logs.offset, 300);
174 assert_eq!(ptr1, logs.buffer.as_ptr());
175 assert_eq!(len1, 300);
176 assert_eq!(ptr2, ptr::null());
177 assert_eq!(len2, 0);
178
179 let (source_offset, ptr1, len1, ptr2, len2) = logs.append(200);
180 assert_eq!(source_offset, 0);
181 assert_eq!(logs.len, 500);
182 assert_eq!(logs.offset, 500);
183 assert_eq!(ptr1, unsafe { logs.buffer.as_ptr().add(300) });
184 assert_eq!(len1, 200);
185 assert_eq!(ptr2, ptr::null());
186 assert_eq!(len2, 0);
187
188 let (source_offset, ptr1, len1, ptr2, len2) = logs.append(600); assert_eq!(source_offset, 0);
190 assert_eq!(logs.len, CAPACITY);
191 assert_eq!(logs.offset, 99); assert_eq!(ptr1, unsafe { logs.buffer.as_ptr().add(500) });
193 assert_eq!(len1, 501);
194 assert_eq!(ptr2, logs.buffer.as_ptr());
195 assert_eq!(len2, 99);
196
197 let (source_offset, ptr1, len1, ptr2, len2) = logs.append(100); assert_eq!(source_offset, 0);
199 assert_eq!(logs.len, CAPACITY);
200 assert_eq!(logs.offset, 199); assert_eq!(ptr1, unsafe { logs.buffer.as_ptr().add(99) });
202 assert_eq!(len1, 100);
203 assert_eq!(ptr2, ptr::null());
204 assert_eq!(len2, 0);
205 }
206}