1use std::io::Write;
2use std::marker::PhantomData;
3use std::num::NonZeroUsize;
4use std::ops::{Add, Rem};
5use std::slice::from_raw_parts_mut;
6use std::{io, mem, ptr};
7
8use crate::ffi;
9use crate::ffi::{vrt_blob, VCL_BLOB, VCL_STRING};
10use crate::vcl::VclError::WsOutOfMemory;
11use crate::vcl::VclResult;
12
13#[derive(Debug)]
16pub struct WsBuffer<'ws, Item, Suffix, Output> {
17 ws: &'ws mut ffi::ws,
19 start_items: *mut Item,
21 unused: &'ws mut [Item],
23
24 _output: PhantomData<Output>,
25 _suffix: PhantomData<Suffix>,
26}
27
28pub type WsStrBuffer<'ws> = WsBuffer<'ws, u8, u8, VCL_STRING>;
29pub type WsBlobBuffer<'ws> = WsBuffer<'ws, u8, vrt_blob, VCL_BLOB>;
30pub type WsTempBuffer<'ws, T> = WsBuffer<'ws, T, (), &'ws [T]>;
31
32impl<Item, Suffix, Output> AsRef<[Item]> for WsBuffer<'_, Item, Suffix, Output> {
33 fn as_ref(&self) -> &[Item] {
35 unsafe { std::slice::from_raw_parts(self.start_items, self.len()) }
36 }
37}
38
39impl<Item, Suffix, Output> AsMut<[Item]> for WsBuffer<'_, Item, Suffix, Output> {
40 fn as_mut(&mut self) -> &mut [Item] {
42 unsafe { from_raw_parts_mut(self.start_items, self.len()) }
43 }
44}
45
46impl<'ws, Item: Copy, Suffix, Output> WsBuffer<'ws, Item, Suffix, Output> {
47 pub(crate) unsafe fn new(ws: &'ws mut ffi::ws) -> VclResult<Self> {
49 let reserved_space = ws.reserve_all() as usize;
50 let raw_start = get_raw_start(ws);
51
52 let items_align = raw_start.align_offset(align_of::<Item>());
54
55 let end = raw_start.add(reserved_space);
57 let suffix_ptr = end.sub(size_of::<Suffix>());
60 let suffix_ptr = suffix_ptr.sub((suffix_ptr as usize).rem(align_of::<Suffix>()));
61 debug_assert!(suffix_ptr.is_aligned(), "suffix_ptr is not aligned");
62 let suffix_size = end.offset_from(suffix_ptr);
63 let suffix_size = usize::try_from(suffix_size).expect("invalid suffix size");
64
65 let items_start = raw_start.add(items_align).cast::<Item>().cast_mut();
66 debug_assert!(items_start.is_aligned(), "WS buffer is not aligned");
67
68 let required = if size_of::<Suffix>() > 0 {
70 items_align + suffix_size
72 } else {
73 items_align + size_of::<Item>()
75 };
76
77 if reserved_space < required {
78 return Err(WsOutOfMemory(NonZeroUsize::new_unchecked(required)));
79 }
80
81 let len = (reserved_space - items_align - suffix_size) / Self::ITEM_SIZE;
82
83 Ok(WsBuffer {
84 ws,
85 start_items: items_start,
86 unused: from_raw_parts_mut(items_start, len),
87 _output: PhantomData,
88 _suffix: PhantomData,
89 })
90 }
91}
92
93impl<Item, Suffix, Output> WsBuffer<'_, Item, Suffix, Output> {
94 const ITEM_SIZE: usize = size_of::<Item>();
95 const _ITEM_SIZE_CHECK: () = assert!(
96 Self::ITEM_SIZE >= size_of::<u8>(),
97 "size_of::<T>() must be at least 1 byte"
98 );
99
100 unsafe fn release(&mut self, preserve: bool) {
105 let start = mem::replace(&mut self.start_items, ptr::null_mut());
106 if !start.is_null() {
107 let preserve_bytes = if preserve {
108 usize::try_from(
110 self.get_suffix_ptr()
111 .cast::<u8>()
112 .offset_from(get_raw_start(self.ws)),
113 )
114 .expect("used_bytes overflow")
115 .add(size_of::<Suffix>())
116 .try_into()
117 .expect("preserve_bytes overflow")
118 } else {
119 0
120 };
121
122 self.ws.release(preserve_bytes);
123 }
124 }
125
126 fn calc_len(start: *const Item, buffer: &[Item]) -> usize {
128 unsafe {
129 let len = buffer.as_ptr().offset_from(start);
130 debug_assert!(len >= 0, "len={len} is negative");
131 len as usize
132 }
133 }
134
135 pub fn len(&self) -> usize {
137 Self::calc_len(self.start_items, self.unused)
138 }
139
140 pub fn is_empty(&self) -> bool {
142 self.len() == 0
143 }
144
145 pub fn remaining(&self) -> usize {
147 self.unused.len()
148 }
149
150 pub fn push(&mut self, item: Item) -> VclResult<()> {
151 if self.unused.is_empty() {
152 return Err(WsOutOfMemory(NonZeroUsize::MIN));
153 }
154 unsafe {
155 let end = self.unused.as_mut_ptr();
156 ptr::write(end, item);
157 self.unused = from_raw_parts_mut(end.add(1), self.unused.len() - 1);
158 }
159 Ok(())
160 }
161
162 pub fn extend_from_slice(&mut self, slice: &[Item]) -> VclResult<()> {
163 if self.unused.len() < slice.len() {
164 return Err(WsOutOfMemory(
165 NonZeroUsize::new(slice.len())
166 .expect("slice.len() is non-zero since it exceeds unused.len()"),
167 ));
168 }
169 unsafe {
170 let end = self.unused.as_mut_ptr();
171 ptr::copy_nonoverlapping(slice.as_ptr(), end, slice.len());
172 self.unused = from_raw_parts_mut(end.add(slice.len()), self.unused.len() - slice.len());
173 }
174 Ok(())
175 }
176
177 unsafe fn get_suffix_ptr(&self) -> *mut Suffix {
179 let ptr_unused = self.unused.as_ptr();
180 let offset = ptr_unused.align_offset(align_of::<Suffix>());
181 ptr_unused.add(offset).cast::<Suffix>().cast_mut()
182 }
183}
184
185impl<Output, Suffix> Write for WsBuffer<'_, u8, Suffix, Output> {
186 fn write(&mut self, data: &[u8]) -> io::Result<usize> {
187 self.unused.write(data)
188 }
189
190 fn flush(&mut self) -> io::Result<()> {
191 Ok(())
192 }
193}
194
195impl<Item, Suffix, Output> Drop for WsBuffer<'_, Item, Suffix, Output> {
196 fn drop(&mut self) {
198 unsafe { self.release(false) };
199 }
200}
201
202impl WsStrBuffer<'_> {
203 pub fn finish(mut self) -> VCL_STRING {
205 unsafe {
206 self.unused.as_mut_ptr().write(b'\0');
210
211 let result = get_raw_start(self.ws).cast();
213
214 self.release(true);
216
217 VCL_STRING(result)
218 }
219 }
220}
221
222impl WsBlobBuffer<'_> {
223 pub fn finish(mut self) -> VCL_BLOB {
225 unsafe {
226 let data = self.as_ref();
227 let suffix_ptr = self.get_suffix_ptr();
229 suffix_ptr.write(vrt_blob {
230 blob: data.as_ptr().cast(),
231 len: data.len(),
232 ..Default::default()
233 });
234
235 self.release(true);
236
237 VCL_BLOB(suffix_ptr)
238 }
239 }
240}
241
242impl<'ws, T> WsTempBuffer<'ws, T> {
243 pub fn finish(mut self) -> &'ws [T] {
245 unsafe {
246 let data = mem::transmute::<&[T], &'ws [T]>(self.as_ref());
248 self.release(true);
249 data
250 }
251 }
252}
253
254fn get_raw_start(ws: &ffi::ws) -> *const u8 {
255 ws.f.cast::<u8>()
256}
257
258#[cfg(test)]
259mod tests {
260 use std::ffi::{CStr, CString};
261
262 use super::*;
263 use crate::vcl::TestWS;
264
265 fn get_cstr(s: &VCL_STRING) -> &CStr {
266 unsafe { CStr::from_ptr(s.0) }
267 }
268
269 fn round_up_to_usize(number: usize) -> usize {
270 number.div_ceil(size_of::<usize>()) * size_of::<usize>()
271 }
272
273 fn buf_to_vec(buf: WsBlobBuffer<'_>) -> &'_ [u8] {
274 let data = buf.finish();
275 let vrt_blob { blob, len, .. } = unsafe { *(data.0) };
276 unsafe { std::slice::from_raw_parts(blob.cast::<u8>(), len) }
277 }
278
279 #[test]
280 fn str_buffer() {
281 let mut test_ws = TestWS::new(160);
282 let mut ws = test_ws.workspace();
283
284 let mut buf = ws
286 .vcl_string_builder()
287 .expect("workspace must have enough space");
288 assert_eq!(buf.remaining(), 159);
289 buf.write_all(b"0123456789").expect("write must succeed");
290 assert_eq!(buf.remaining(), 149);
291 assert_eq!(get_cstr(&buf.finish()), c"0123456789");
293
294 let mut buf = ws
295 .vcl_string_builder()
296 .expect("workspace must have enough space");
297 assert_eq!(buf.remaining(), 160 - round_up_to_usize(10 + 1) - 1);
298 write!(buf, "this data is ignored").expect("write must succeed");
299 drop(buf);
302
303 let mut buf = ws
304 .vcl_string_builder()
305 .expect("workspace must have enough space");
306 assert_eq!(buf.remaining(), 160 - round_up_to_usize(10 + 1) - 1);
307 let fill = vec![b'x'; buf.remaining() - 1];
308 buf.write_all(&fill).expect("write must succeed");
309 assert_eq!(buf.remaining(), 1);
310 assert_eq!(
311 get_cstr(&buf.finish()),
312 CString::new(fill)
313 .expect("fill bytes must not contain null bytes")
314 .as_c_str()
315 );
316
317 assert!(matches!(ws.vcl_string_builder(), Err(WsOutOfMemory(_))));
318
319 let mut test_ws = TestWS::new(160);
321 let mut ws = test_ws.workspace();
322 let mut buf = ws
323 .vcl_string_builder()
324 .expect("workspace must have enough space");
325 assert_eq!(buf.remaining(), 159);
326 let fill = vec![b'x'; buf.remaining()];
327 buf.write_all(&fill).expect("write must succeed");
328 assert_eq!(buf.remaining(), 0);
329 assert_eq!(
330 get_cstr(&buf.finish()),
331 CString::new(fill)
332 .expect("fill bytes must not contain null bytes")
333 .as_c_str()
334 );
335
336 assert!(matches!(ws.vcl_string_builder(), Err(WsOutOfMemory(_))));
337 }
338
339 #[test]
340 fn blob_buffer() {
341 assert_eq!(size_of::<vrt_blob>(), 24);
342 assert_eq!(align_of::<vrt_blob>(), 8);
343
344 let mut test_ws = TestWS::new(160);
346 let mut ws = test_ws.workspace();
347
348 let mut buf = ws
350 .vcl_blob_builder()
351 .expect("workspace must have enough space");
352 assert_eq!(buf.remaining(), 160 - 24);
353 buf.write_all(b"0123456789").expect("write must succeed");
354 let used = round_up_to_usize(24 + 10);
355 let data = buf_to_vec(buf);
356 assert_eq!(data, b"0123456789");
357
358 let mut buf = ws
360 .vcl_blob_builder()
361 .expect("workspace must have enough space");
362 assert_eq!(buf.remaining(), 160 - used - 24);
363 write!(buf, "this data is ignored").expect("write must succeed");
364 drop(buf);
365
366 assert_eq!(data, b"0123456789");
368
369 let mut buf = ws
372 .vcl_blob_builder()
373 .expect("workspace must have enough space");
374 assert_eq!(buf.remaining(), 160 - used - 24);
375 write!(buf, "this data is ignored").expect("write must succeed");
376 }
377
378 #[repr(C)]
379 #[derive(Debug, PartialEq, Clone, Copy)]
380 struct TestStruct(u16, u8);
381
382 #[test]
383 fn temp_buffer() {
384 assert_eq!(4, size_of::<TestStruct>());
385 let mut test_ws = TestWS::new(160);
386 let mut ws = test_ws.workspace();
387
388 let mut buf = ws
390 .slice_builder::<TestStruct>()
391 .expect("workspace must have enough space");
392 assert_eq!(buf.remaining(), 160 / 4);
393 buf.push(TestStruct(1, 2)).expect("push must succeed");
394 let used = round_up_to_usize(4);
395 let data = buf.finish();
396 assert_eq!(data, [TestStruct(1, 2)]);
397
398 let mut buf = ws
400 .slice_builder()
401 .expect("workspace must have enough space");
402 assert_eq!(buf.remaining(), 160 - used);
403 write!(buf, "this data is ignored").expect("write must succeed");
404 drop(buf);
405
406 assert_eq!(data, [TestStruct(1, 2)]);
408
409 let mut buf = ws
411 .slice_builder()
412 .expect("workspace must have enough space");
413 assert_eq!(buf.remaining(), 160 - used);
414 buf.extend_from_slice(b"0123456789")
415 .expect("extend_from_slice must succeed");
416 assert_eq!(buf.finish(), b"0123456789");
417 }
418}