varnish_sys/vcl/
ws_str_buffer.rs

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::VCL_STRING;
10#[cfg(not(varnishsys_6))]
11use crate::ffi::{vrt_blob, VCL_BLOB};
12use crate::vcl::VclError::WsOutOfMemory;
13use crate::vcl::VclResult;
14
15/// The free region of the workspace that functions as a "resizable" vector, up to the end of the workspace.
16/// The buffer must be finalized using `finish()` to avoid being reclaimed when dropped.
17#[derive(Debug)]
18pub struct WsBuffer<'ws, Item, Suffix, Output> {
19    /// The workspace pointer, used to release the workspace
20    ws: &'ws mut ffi::ws,
21    /// The start of the writable buffer, aligned to the content type. Will set to null when finished.
22    start_items: *mut Item,
23    /// The reserved buffer will move its start as we write to it, thus becoming "used"
24    unused: &'ws mut [Item],
25
26    _output: PhantomData<Output>,
27    _suffix: PhantomData<Suffix>,
28}
29
30pub type WsStrBuffer<'ws> = WsBuffer<'ws, u8, u8, VCL_STRING>;
31#[cfg(not(varnishsys_6))]
32pub type WsBlobBuffer<'ws> = WsBuffer<'ws, u8, vrt_blob, VCL_BLOB>;
33pub type WsTempBuffer<'ws, T> = WsBuffer<'ws, T, (), &'ws [T]>;
34
35impl<Item, Suffix, Output> AsRef<[Item]> for WsBuffer<'_, Item, Suffix, Output> {
36    /// Get the written data as a slice
37    fn as_ref(&self) -> &[Item] {
38        unsafe { std::slice::from_raw_parts(self.start_items, self.len()) }
39    }
40}
41
42impl<Item, Suffix, Output> AsMut<[Item]> for WsBuffer<'_, Item, Suffix, Output> {
43    /// Get the writable buffer as a slice
44    fn as_mut(&mut self) -> &mut [Item] {
45        unsafe { from_raw_parts_mut(self.start_items, self.len()) }
46    }
47}
48
49impl<'ws, Item: Copy, Suffix, Output> WsBuffer<'ws, Item, Suffix, Output> {
50    /// Internal method to create a new buffer
51    pub(crate) unsafe fn new(ws: &'ws mut ffi::ws) -> VclResult<Self> {
52        let reserved_space = ws.reserve_all() as usize;
53        let raw_start = get_raw_start(ws);
54
55        // Compute the size of the alignment, usually compile-time zero, but just in case
56        let items_align = raw_start.align_offset(align_of::<Item>());
57
58        // Computes how many bytes need to be reserved for the suffix
59        let end = raw_start.add(reserved_space);
60        // last byte where Suffix can start and fit entirely,
61        // rounded down to the alignment
62        let suffix_ptr = end.sub(size_of::<Suffix>());
63        let suffix_ptr = suffix_ptr.sub((suffix_ptr as usize).rem(align_of::<Suffix>()));
64        debug_assert!(suffix_ptr.is_aligned(), "suffix_ptr is not aligned");
65        let suffix_size = end.offset_from(suffix_ptr);
66        let suffix_size = usize::try_from(suffix_size).expect("invalid suffix size");
67
68        let items_start = raw_start.add(items_align).cast::<Item>().cast_mut();
69        debug_assert!(items_start.is_aligned(), "WS buffer is not aligned");
70
71        // Minimum space we need to function properly. A zero-length null-terminated C-string is valid.
72        let required = if size_of::<Suffix>() > 0 {
73            // With the suffix, it's enough to have space for the suffix itself, i.e. `\0`
74            items_align + suffix_size
75        } else {
76            // Without the suffix, require space for at least one item
77            items_align + size_of::<Item>()
78        };
79
80        if reserved_space < required {
81            return Err(WsOutOfMemory(NonZeroUsize::new_unchecked(required)));
82        }
83
84        let len = (reserved_space - items_align - suffix_size) / Self::ITEM_SIZE;
85
86        Ok(WsBuffer {
87            ws,
88            start_items: items_start,
89            unused: from_raw_parts_mut(items_start, len),
90            _output: PhantomData,
91            _suffix: PhantomData,
92        })
93    }
94}
95
96impl<Item, Suffix, Output> WsBuffer<'_, Item, Suffix, Output> {
97    const ITEM_SIZE: usize = size_of::<Item>();
98    const _ITEM_SIZE_CHECK: () = assert!(
99        Self::ITEM_SIZE >= size_of::<u8>(),
100        "size_of::<T>() must be at least 1 byte"
101    );
102
103    /// Release the workspace, reclaiming the memory except for the written data.
104    ///
105    /// Safety:
106    ///     This must be the last call before dropping. It may be called multiple times.
107    unsafe fn release(&mut self, preserve: bool) {
108        let start = mem::replace(&mut self.start_items, ptr::null_mut());
109        if !start.is_null() {
110            let preserve_bytes = if preserve {
111                // compute total bytes used by the buffer
112                usize::try_from(
113                    self.get_suffix_ptr()
114                        .cast::<u8>()
115                        .offset_from(get_raw_start(self.ws)),
116                )
117                .expect("used_bytes overflow")
118                .add(size_of::<Suffix>())
119                .try_into()
120                .expect("preserve_bytes overflow")
121            } else {
122                0
123            };
124
125            self.ws.release(preserve_bytes);
126        }
127    }
128
129    /// Internal method to calculate the length of the written data
130    fn calc_len(start: *const Item, buffer: &[Item]) -> usize {
131        unsafe {
132            let len = buffer.as_ptr().offset_from(start);
133            debug_assert!(len >= 0, "len={len} is negative");
134            len as usize
135        }
136    }
137
138    /// Get the length of the written data
139    pub fn len(&self) -> usize {
140        Self::calc_len(self.start_items, self.unused)
141    }
142
143    /// Check if anything has been written to the buffer
144    pub fn is_empty(&self) -> bool {
145        self.len() == 0
146    }
147
148    /// Get the remaining capacity of the buffer
149    pub fn remaining(&self) -> usize {
150        self.unused.len()
151    }
152
153    pub fn push(&mut self, item: Item) -> VclResult<()> {
154        if self.unused.is_empty() {
155            return Err(WsOutOfMemory(NonZeroUsize::MIN));
156        }
157        unsafe {
158            let end = self.unused.as_mut_ptr();
159            ptr::write(end, item);
160            self.unused = from_raw_parts_mut(end.add(1), self.unused.len() - 1);
161        }
162        Ok(())
163    }
164
165    pub fn extend_from_slice(&mut self, slice: &[Item]) -> VclResult<()> {
166        if self.unused.len() < slice.len() {
167            return Err(WsOutOfMemory(NonZeroUsize::new(slice.len()).unwrap()));
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    /// Get the pointer to the allowed suffix location right after currently used data.
178    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    /// Ignore all the write commands, reclaiming the workspace memory
197    fn drop(&mut self) {
198        unsafe { self.release(false) };
199    }
200}
201
202impl WsStrBuffer<'_> {
203    /// Finish writing to the [`WsBuffer`], returning the allocated [`VCL_STRING`].
204    pub fn finish(mut self) -> VCL_STRING {
205        unsafe {
206            // SAFETY:
207            // Since we reserved one extra byte for the NUL terminator,
208            // we can force write the NUL terminator even past the end of the slice.
209            self.unused.as_mut_ptr().write(b'\0');
210
211            // Must get the result before releasing the workspace, as it updates the pointer
212            let result = get_raw_start(self.ws).cast();
213
214            // Reserve written data including the NUL terminator, and release the rest
215            self.release(true);
216
217            VCL_STRING(result)
218        }
219    }
220}
221
222#[cfg(not(varnishsys_6))]
223impl WsBlobBuffer<'_> {
224    /// Finish writing to the [`WsBlobBuffer`], returning the allocated [`VCL_BLOB`].
225    pub fn finish(mut self) -> VCL_BLOB {
226        unsafe {
227            let data = self.as_ref();
228            // Create vrt_blob suffix right after the data
229            let suffix_ptr = self.get_suffix_ptr();
230            suffix_ptr.write(vrt_blob {
231                blob: data.as_ptr().cast(),
232                len: data.len(),
233                ..Default::default()
234            });
235
236            self.release(true);
237
238            VCL_BLOB(suffix_ptr)
239        }
240    }
241}
242
243impl<'ws, T> WsTempBuffer<'ws, T> {
244    /// Finish writing to the [`WsTempBuffer`], returning the allocated `&'ws [T]`.
245    pub fn finish(mut self) -> &'ws [T] {
246        unsafe {
247            // force lifetime to be 'ws because we are dropping self
248            let data = mem::transmute::<&[T], &'ws [T]>(self.as_ref());
249            self.release(true);
250            data
251        }
252    }
253}
254
255fn get_raw_start(ws: &ffi::ws) -> *const u8 {
256    ws.f.cast::<u8>()
257}
258
259#[cfg(test)]
260mod tests {
261    use std::ffi::{CStr, CString};
262
263    use super::*;
264    use crate::vcl::TestWS;
265
266    fn get_cstr(s: &VCL_STRING) -> &CStr {
267        unsafe { CStr::from_ptr(s.0) }
268    }
269
270    fn round_up_to_usize(number: usize) -> usize {
271        number.div_ceil(size_of::<usize>()) * size_of::<usize>()
272    }
273
274    #[cfg(not(varnishsys_6))]
275    fn buf_to_vec(buf: WsBlobBuffer) -> &[u8] {
276        let data = buf.finish();
277        let vrt_blob { blob, len, .. } = unsafe { *(data.0) };
278        unsafe { std::slice::from_raw_parts(blob.cast::<u8>(), len) }
279    }
280
281    #[test]
282    fn str_buffer() {
283        let mut test_ws = TestWS::new(160);
284        let mut ws = test_ws.workspace();
285
286        // first buffer call gets all available space
287        let mut buf = ws.vcl_string_builder().unwrap();
288        assert_eq!(buf.remaining(), 159);
289        buf.write_all(b"0123456789").unwrap();
290        assert_eq!(buf.remaining(), 149);
291        // saving 10 bytes + nul
292        assert_eq!(get_cstr(&buf.finish()), c"0123456789");
293
294        let mut buf = ws.vcl_string_builder().unwrap();
295        assert_eq!(buf.remaining(), 160 - round_up_to_usize(10 + 1) - 1);
296        write!(buf, "this data is ignored").unwrap();
297        // the ReservedBuf goes out of scope without a call to .finish()
298        // so now data is fully allocated
299        drop(buf);
300
301        let mut buf = ws.vcl_string_builder().unwrap();
302        assert_eq!(buf.remaining(), 160 - round_up_to_usize(10 + 1) - 1);
303        let fill = vec![b'x'; buf.remaining() - 1];
304        buf.write_all(&fill).unwrap();
305        assert_eq!(buf.remaining(), 1);
306        assert_eq!(
307            get_cstr(&buf.finish()),
308            CString::new(fill).unwrap().as_c_str()
309        );
310
311        assert!(matches!(ws.vcl_string_builder(), Err(WsOutOfMemory(_))));
312
313        // Will to the end of the buffer
314        let mut test_ws = TestWS::new(160);
315        let mut ws = test_ws.workspace();
316        let mut buf = ws.vcl_string_builder().unwrap();
317        assert_eq!(buf.remaining(), 159);
318        let fill = vec![b'x'; buf.remaining()];
319        buf.write_all(&fill).unwrap();
320        assert_eq!(buf.remaining(), 0);
321        assert_eq!(
322            get_cstr(&buf.finish()),
323            CString::new(fill).unwrap().as_c_str()
324        );
325
326        assert!(matches!(ws.vcl_string_builder(), Err(WsOutOfMemory(_))));
327    }
328
329    #[test]
330    #[cfg(not(varnishsys_6))]
331    fn blob_buffer() {
332        assert_eq!(size_of::<vrt_blob>(), 24);
333        assert_eq!(align_of::<vrt_blob>(), 8);
334
335        // init a workspace
336        let mut test_ws = TestWS::new(160);
337        let mut ws = test_ws.workspace();
338
339        // first buffer call gets all available space
340        let mut buf = ws.vcl_blob_builder().unwrap();
341        assert_eq!(buf.remaining(), 160 - 24);
342        buf.write_all(b"0123456789").unwrap();
343        let used = round_up_to_usize(24 + 10);
344        let data = buf_to_vec(buf);
345        assert_eq!(data, b"0123456789");
346
347        // second reservation without (header + )
348        let mut buf = ws.vcl_blob_builder().unwrap();
349        assert_eq!(buf.remaining(), 160 - used - 24);
350        write!(buf, "this data is ignored").unwrap();
351        drop(buf);
352
353        // validate no data corruption
354        assert_eq!(data, b"0123456789");
355
356        // the ReservedBuf goes out of scope without a call to .finish()
357        // so now data is fully allocated
358        let mut buf = ws.vcl_blob_builder().unwrap();
359        assert_eq!(buf.remaining(), 160 - used - 24);
360        write!(buf, "this data is ignored").unwrap();
361    }
362
363    #[repr(C)]
364    #[derive(Debug, PartialEq, Clone, Copy)]
365    struct TestStruct(u16, u8);
366
367    #[test]
368    fn temp_buffer() {
369        assert_eq!(4, size_of::<TestStruct>());
370        let mut test_ws = TestWS::new(160);
371        let mut ws = test_ws.workspace();
372
373        // first buffer call gets all available space
374        let mut buf = ws.slice_builder::<TestStruct>().unwrap();
375        assert_eq!(buf.remaining(), 160 / 4);
376        buf.push(TestStruct(1, 2)).unwrap();
377        let used = round_up_to_usize(4);
378        let data = buf.finish();
379        assert_eq!(data, [TestStruct(1, 2)]);
380
381        // second reservation without (header + )
382        let mut buf = ws.slice_builder().unwrap();
383        assert_eq!(buf.remaining(), 160 - used);
384        write!(buf, "this data is ignored").unwrap();
385        drop(buf);
386
387        // validate no data corruption
388        assert_eq!(data, [TestStruct(1, 2)]);
389
390        // buf went out of scope without a call to .finish(), discarding it
391        let mut buf = ws.slice_builder().unwrap();
392        assert_eq!(buf.remaining(), 160 - used);
393        buf.extend_from_slice(b"0123456789").unwrap();
394        assert_eq!(buf.finish(), b"0123456789");
395    }
396}