Skip to main content

vpp_plugin/vlib/
buffer.rs

1//! vlib buffer abstraction
2//!
3//! This module contains abstractions around VPP's `vlib_buffer_t` structure.
4//! It provides safe access to buffer fields and operations.
5//! It also includes buffer allocation and deallocation functions.
6//! The goal is to provide a safe and ergonomic interface for working with VPP buffers.
7
8use std::{fmt, hint::assert_unchecked, mem::ManuallyDrop, mem::MaybeUninit};
9
10use arrayvec::ArrayVec;
11use bitflags::bitflags;
12
13use crate::{
14    bindings::{
15        CLIB_LOG2_CACHE_LINE_BYTES, VLIB_BUFFER_EXT_HDR_VALID, VLIB_BUFFER_IS_TRACED,
16        VLIB_BUFFER_MIN_CHAIN_SEG_SIZE, VLIB_BUFFER_NEXT_PRESENT, VLIB_BUFFER_PRE_DATA_SIZE,
17        VLIB_BUFFER_TOTAL_LENGTH_VALID, vlib_add_trace, vlib_buffer_func_main, vlib_buffer_t,
18        vlib_buffer_t__bindgen_ty_1, vlib_buffer_t__bindgen_ty_1__bindgen_ty_1__bindgen_ty_1,
19        vlib_helper_buffer_alloc, vlib_helper_buffer_free,
20    },
21    const_assert,
22    vlib::{
23        self, MainRef,
24        node::{ErrorCounters, Node, NodeRuntimeRef, VectorBufferIndex},
25    },
26    vppinfra::{
27        cache::{prefetch_load, prefetch_store},
28        likely,
29    },
30};
31
32/// VPP buffer index
33#[repr(transparent)]
34#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
35pub struct BufferIndex(u32);
36
37impl BufferIndex {
38    /// Construct a new `BufferIndex`
39    pub const fn new(buffer: u32) -> Self {
40        Self(buffer)
41    }
42}
43
44impl From<u32> for BufferIndex {
45    fn from(value: u32) -> BufferIndex {
46        Self(value)
47    }
48}
49
50impl From<BufferIndex> for u32 {
51    fn from(value: BufferIndex) -> Self {
52        value.0
53    }
54}
55
56impl VectorBufferIndex for BufferIndex {
57    fn as_u32_slice(slice: &[Self]) -> &[u32] {
58        // SAFETY: BufferIndex is a repr(transparent) wrapper around u32 so the src and dst slice
59        // types have the same memory layout
60        unsafe { std::mem::transmute::<&[BufferIndex], &[u32]>(slice) }
61    }
62}
63
64bitflags! {
65    /// vlib buffer flags
66    #[repr(transparent)]
67    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
68    pub struct BufferFlags: u32 {
69        /// Trace this buffer
70        const IS_TRACED = VLIB_BUFFER_IS_TRACED;
71        /// This is one buffer in a chain of buffers
72        const NEXT_PRESENT = VLIB_BUFFER_NEXT_PRESENT;
73        /// Total length is valid
74        const TOTAL_LENGTH_VALID = VLIB_BUFFER_TOTAL_LENGTH_VALID;
75        /// Contains external buffer manager header
76        const EXT_HDR_VALID = VLIB_BUFFER_EXT_HDR_VALID;
77
78        // Flags can be extended by user/vnet
79        const _ = !0;
80    }
81}
82
83/// Construct a user buffer flag
84///
85/// `n` must be less than 29 and greater than 0.
86pub const fn vlib_buffer_flag_user(n: u32) -> u32 {
87    assert!(n < 29 && n > 0);
88    1 << (32 - n)
89}
90
91/// Reference to a VPP buffer
92///
93/// A `&mut BufferRef<FeatureData>` is equivalent to a `vlib_buffer_t *` in C (a `*mut
94/// vlib_buffer_t` in Rust).
95#[repr(transparent)]
96pub struct BufferRef<FeatureData>(foreign_types::Opaque, std::marker::PhantomData<FeatureData>);
97
98impl<FeatureData> BufferRef<FeatureData> {
99    /// Create a `&BufferRef` from a raw pointer
100    ///
101    /// # Safety
102    ///
103    /// - The pointer must be a valid and properly initialised `vlib_buffer_t`.
104    /// - The pointer must stay valid and the contents must not be mutated for the duration of the
105    ///   lifetime of the returned object.
106    #[inline(always)]
107    pub unsafe fn from_ptr<'a>(ptr: *mut vlib_buffer_t) -> &'a Self {
108        // SAFETY: The safety requirements are documented in the function's safety comment.
109        unsafe { &*(ptr as *mut _) }
110    }
111
112    /// Create a `&mut BufferRef` from a raw pointer
113    ///
114    /// # Safety
115    ///
116    /// - The pointer must be a valid and properly initialised `vlib_buffer_t`.
117    /// - The pointer must stay valid and the contents must not be mutated for the duration of the
118    ///   lifetime of the returned object.
119    #[inline(always)]
120    pub unsafe fn from_ptr_mut<'a>(ptr: *mut vlib_buffer_t) -> &'a mut Self {
121        // SAFETY: The safety requirements are documented in the function's safety comment.
122        unsafe { &mut *(ptr as *mut _) }
123    }
124
125    /// Returns the raw pointer to the underlying `vlib_buffer_t`
126    #[inline(always)]
127    pub fn as_ptr(&self) -> *mut vlib_buffer_t {
128        self as *const _ as *mut _
129    }
130
131    #[inline(always)]
132    fn as_details(&self) -> &vlib_buffer_t__bindgen_ty_1 {
133        // SAFETY: since the reference to self is valid, so must be the pointer and it's safe to
134        // use the __bindgen_anon_1 union arm since the union is just present to force alignment
135        // Creation preconditions mean there are no aliased accesses to the buffer so it's fine
136        // to take a reference
137        unsafe { (*self.as_ptr()).__bindgen_anon_1.as_ref() }
138    }
139
140    #[inline(always)]
141    fn as_details_mut(&mut self) -> &mut vlib_buffer_t__bindgen_ty_1 {
142        // SAFETY: since the reference to self is valid, so must be the pointer and it's safe to
143        // use the __bindgen_anon_1 union arm since the union is just present to force alignment.
144        // Creation preconditions mean there are no aliased accesses to the buffer so it's fine
145        // to take a reference
146        unsafe { (*self.as_ptr()).__bindgen_anon_1.as_mut() }
147    }
148
149    #[inline(always)]
150    pub(crate) fn as_metadata(&self) -> &vlib_buffer_t__bindgen_ty_1__bindgen_ty_1__bindgen_ty_1 {
151        // SAFETY: since the reference to self is valid, so must be the pointer and it's safe to
152        // use the __bindgen_anon_1 union arm since the union is just present to force alignment
153        // Creation preconditions mean there are no aliased accesses to the buffer so it's fine
154        // to take a reference
155        unsafe { self.as_details().__bindgen_anon_1.__bindgen_anon_1.as_ref() }
156    }
157
158    #[inline(always)]
159    pub(crate) fn as_metadata_mut(
160        &mut self,
161    ) -> &mut vlib_buffer_t__bindgen_ty_1__bindgen_ty_1__bindgen_ty_1 {
162        // SAFETY: since the reference to self is valid, so must be the pointer and it's safe to
163        // use the __bindgen_anon_1 union arm since the union is just present to force alignment.
164        // Creation preconditions mean there are no aliased accesses to the buffer so it's fine
165        // to take a reference
166        unsafe {
167            self.as_details_mut()
168                .__bindgen_anon_1
169                .__bindgen_anon_1
170                .as_mut()
171        }
172    }
173
174    fn data(&self) -> *const u8 {
175        self.as_details().data.as_ptr()
176    }
177
178    fn current_data_offset(&self) -> i16 {
179        self.as_metadata().current_data
180    }
181
182    fn current_data_offset_mut(&mut self) -> &mut i16 {
183        &mut self.as_metadata_mut().current_data
184    }
185
186    /// Current length
187    ///
188    /// Typically, this is the amount of packet data remaining from [`Self::current_ptr_mut`].
189    pub fn current_length(&self) -> u16 {
190        self.as_metadata().current_length
191    }
192
193    fn current_length_mut(&mut self) -> &mut u16 {
194        &mut self.as_metadata_mut().current_length
195    }
196
197    /// Get the flags set for this buffer
198    #[inline(always)]
199    pub fn flags(&self) -> BufferFlags {
200        BufferFlags::from_bits_retain(self.as_metadata().flags)
201    }
202
203    /// Set the flags for this buffer
204    ///
205    /// # Safety
206    ///
207    /// [`BufferFlags::NEXT_PRESENT`] must not be set unless there is a next buffer in the chain.
208    /// [`BufferFlags::EXT_HDR_VALID`] must not be set or cleared unless the external buffer manager header is valid
209    /// or not valid respectively.
210    ///
211    #[inline(always)]
212    pub unsafe fn set_flags(&mut self, flags: BufferFlags) {
213        self.as_metadata_mut().flags = flags.bits()
214    }
215
216    /// Get a pointer to the current data
217    ///
218    /// This corresponds to the VPP C API `vlib_buffer_get_current`.
219    ///
220    /// # Usage guidance
221    ///
222    /// Note that the pointer returned may point to uninitialised data depending on the context.
223    /// In addition, depending on the context, the remaining data the amount is expected.
224    /// Finally, if remaining data is sufficent and it's initialised it may not have been validated
225    /// so care must be taken in determining whether or not lengths in the headers can be trusted.
226    pub fn current_ptr_mut(&mut self) -> *mut u8 {
227        let data = self.data().cast_mut();
228        let current_data = self.current_data_offset();
229
230        debug_assert!(current_data >= -(VLIB_BUFFER_PRE_DATA_SIZE as i16));
231
232        // SAFETY: current_data is asserted to be valid and point into valid (but possibly
233        // unintialised) data or pre_data.
234        unsafe { data.offset(current_data as isize) }
235    }
236
237    /// Check if the buffer has space to advance `l` bytes
238    ///
239    /// This corresponds to the VPP C API `vlib_buffer_has_space`.
240    pub fn has_space(&self, l: i16) -> bool {
241        self.current_length() >= l as u16
242    }
243
244    /// Advance the current data pointer by `l` bytes
245    ///
246    /// This corresponds to the VPP C API `vlib_buffer_advance`.
247    ///
248    /// # Safety
249    ///
250    /// - If `l` is positive, the buffer must have at least `l` bytes of data remaining.
251    /// - If `l` is negative, the current data offset must be at least `-l` bytes from the start of
252    ///   the buffer's data area (including pre-data).
253    pub unsafe fn advance(&mut self, l: i16) {
254        debug_assert!(l < 0 || self.current_length() >= l as u16);
255        debug_assert!(
256            l >= 0 || self.current_data_offset() + VLIB_BUFFER_PRE_DATA_SIZE as i16 >= -l
257        );
258
259        *self.current_data_offset_mut() += l;
260        if l >= 0 {
261            *self.current_length_mut() -= l as u16;
262        } else {
263            *self.current_length_mut() += -l as u16;
264        }
265
266        debug_assert!(
267            !self.flags().contains(BufferFlags::NEXT_PRESENT)
268                || self.current_length() >= VLIB_BUFFER_MIN_CHAIN_SEG_SIZE as u16
269        );
270    }
271
272    /// Append uninitialised data to the end of the current data.
273    ///
274    /// Returns a pointer to the start of the newly appended uninitialised data.
275    ///
276    /// This corresponds to the VPP C function `vlib_buffer_put_uninit`.
277    ///
278    /// # Safety
279    ///
280    /// The current data plus the space requested must not exceed the data size of the buffer
281    /// given during allocation. See [`super::MainRef::buffer_default_data_size`] for buffers
282    /// allocated by [`super::MainRef::alloc_buffer`].
283    ///
284    /// The caller must ensure that the data is correctly initialised before passing the buffer to
285    /// code that assumes it is correctly initialised, such as enqueing the buffer another node.
286    pub unsafe fn put_uninit(&mut self, size: u16) -> *mut u8 {
287        let p = self.tail_mut();
288        *self.current_length_mut() += size;
289        p
290    }
291
292    /// Get a pointer to the end of the current data
293    ///
294    /// This corresponds to the VPP C function `vlib_buffer_get_tail`.
295    pub fn tail_mut(&mut self) -> *mut u8 {
296        let data = self.data().cast_mut();
297        let current_data = self.current_data_offset();
298
299        debug_assert!(current_data >= -(VLIB_BUFFER_PRE_DATA_SIZE as i16));
300
301        // SAFETY: current_data and current_length are asserted to be valid and `current_data +
302        // current_length` asserted to point to the end of valid (but possibly unintialised) data
303        // or pre_data.
304        unsafe {
305            let ptr = data.offset(current_data as isize);
306            ptr.add(self.current_length() as usize)
307        }
308    }
309
310    /// Add trace data to this buffer
311    pub fn add_trace<N: Node>(
312        &mut self,
313        vm: &MainRef,
314        node: &NodeRuntimeRef<N>,
315    ) -> &mut MaybeUninit<N::TraceData> {
316        // SAFETY: pointers are valid and the uninitialised data that is returned cannot be read
317        // by safe code
318        unsafe {
319            &mut *(vlib_add_trace(
320                vm.as_ptr(),
321                node.as_ptr(),
322                self.as_ptr(),
323                std::mem::size_of::<N::TraceData>() as u32,
324            ) as *mut MaybeUninit<N::TraceData>)
325        }
326    }
327
328    /// Set an error reason
329    ///
330    /// This is typically done before sending the packet to the `drop` node, where it use the
331    /// value to display the reason in traces and automatically increment the per-node, per-error
332    /// counter for the error.
333    pub fn set_error<N: Node>(&mut self, node: &NodeRuntimeRef<N>, error: N::Errors) {
334        // SAFETY: vlib_node_runtime_t::errors is sized according to the number of error values
335        // and it is a precondition of the Errors trait that the value return by into_u16() cannot
336        // be greater than or equal to the declared number of error values.
337        unsafe {
338            let error_value = (*node.as_ptr()).errors.add(error.into_u16() as usize);
339            self.as_metadata_mut().error = *error_value;
340        }
341    }
342
343    /// Get the total length of the buffer chain not including the first buffer
344    #[inline(always)]
345    pub fn total_length_not_including_first_buffer(&self) -> u32 {
346        debug_assert!(self.flags().contains(BufferFlags::TOTAL_LENGTH_VALID));
347        self.as_details().total_length_not_including_first_buffer
348    }
349
350    /// Get the total length of the buffer chain from the current offset
351    ///
352    /// Note that this doesn't take into account any bytes that have been [`Self::advance()`]d
353    /// over.
354    #[inline(always)]
355    pub fn length_in_chain(&self, vm: &vlib::MainRef) -> u64 {
356        let len = self.current_length();
357
358        if likely(!self.flags().contains(BufferFlags::NEXT_PRESENT)) {
359            return len as u64;
360        }
361
362        if likely(self.flags().contains(BufferFlags::TOTAL_LENGTH_VALID)) {
363            return len as u64 + self.total_length_not_including_first_buffer() as u64;
364        }
365
366        // SAFETY: The buffer pointer is valid and the function is called in a valid context.
367        unsafe {
368            crate::bindings::vlib_buffer_length_in_chain_slow_path(vm.as_ptr(), self.as_ptr())
369        }
370    }
371
372    /// Hint to the CPU to prefetch the buffer header for read access.
373    ///
374    /// This is a performance hint that attempts to bring the buffer header into the CPU cache
375    /// prior to reading fields from it. It does not affect program semantics and may be a no-op
376    /// on some platforms. Use this when you will shortly read header fields and want to reduce
377    /// cache miss latency.
378    pub fn prefetch_header_load(&self) {
379        prefetch_load(self.as_ptr());
380    }
381
382    /// Hint to the CPU to prefetch the buffer header for write access.
383    ///
384    /// Similar to `prefetch_header_load` but indicates imminent writes to the header. This is a
385    /// performance optimization only and does not change observable behaviour other than timing.
386    pub fn prefetch_header_store(&self) {
387        prefetch_store(self.as_ptr());
388    }
389
390    /// Hint to the CPU to prefetch the buffer data area for read access.
391    ///
392    /// This brings the buffer's data into cache in preparation for reading the packet payload.
393    /// It is a non-semantic performance hint and may be ignored on some architectures. Use this
394    /// when you will shortly read packet data and want to reduce cache miss latency.
395    pub fn prefetch_data_load(&self) {
396        prefetch_load(&self.as_details().data);
397    }
398
399    /// Hint to the CPU to prefetch the buffer data area for write access.
400    ///
401    /// Similar to `prefetch_data_load` but indicates the caller will write into the data area.
402    /// This is a cache-warming hint to reduce latency on subsequent stores.
403    pub fn prefetch_data_store(&self) {
404        prefetch_store(&self.as_details().data);
405    }
406}
407
408/// Owned buffer (with context)
409///
410/// The `&MainRef` context is necessary to be able to free the buffer on drop.
411pub struct BufferWithContext<'a> {
412    buffer: u32,
413    vm: &'a MainRef,
414}
415
416impl<'a> BufferWithContext<'a> {
417    /// Creates a `BufferWithContext` directly from a buffer index and a main reference
418    ///
419    /// # Safety
420    /// - The buffer index must be valid and the caller must have ownership of the buffer it
421    ///   corresponds to.
422    pub unsafe fn from_parts(buffer: BufferIndex, vm: &'a MainRef) -> Self {
423        Self {
424            buffer: buffer.0,
425            vm,
426        }
427    }
428
429    /// Decomposes a `BufferWithContext` into its component parts
430    ///
431    /// After calling this the caller is responsible for ensuring the buffer gets freed, either
432    /// by calling [`BufferWithContext::from_parts`] or by passing it into another function which
433    /// takes ownership of it and eventually causes it to be freed.
434    pub fn into_parts(self) -> (BufferIndex, &'a MainRef) {
435        let me = ManuallyDrop::new(self);
436        (BufferIndex(me.buffer), me.vm)
437    }
438
439    /// Get a mutable reference to the buffer
440    pub fn as_buffer_ref(&mut self) -> &mut BufferRef<()> {
441        let from = &[self.buffer];
442        let mut b: ArrayVec<_, 1> = ArrayVec::new();
443        // SAFETY: capacity of b equals the length of from, `self.buffer` is a valid index and we
444        // force FeatureData to `()` since it isn't known and the buffer cannot be part of a
445        // feature arc.
446        unsafe {
447            self.vm.get_buffers(from, &mut b);
448        }
449        b.remove(0)
450    }
451}
452
453impl Drop for BufferWithContext<'_> {
454    fn drop(&mut self) {
455        // SAFETY: we have a reference to MainRef so the pointer must be valid, we pass in a
456        // pointer to buffers consistent with the number of buffers passed in, and self.buffer
457        // is a valid buffer index that we have ownership of.
458        unsafe {
459            vlib_helper_buffer_free(self.vm.as_ptr(), &mut self.buffer, 1);
460        }
461    }
462}
463
464/// Buffer allocation error
465#[derive(Copy, Clone, PartialEq, Eq, Debug)]
466pub struct BufferAllocError;
467
468impl fmt::Display for BufferAllocError {
469    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
470        write!(f, "buffer allocation error")
471    }
472}
473
474impl std::error::Error for BufferAllocError {}
475
476/// u64 x 8
477///
478/// This type exists to strongly hint to the compiler that it should emit vector instructions.
479///
480/// In the future, the implementation might be changed to use standard library portable SIMD once
481/// stabilised (https://github.com/rust-lang/rust/issues/86656), or use arch-specific intrinsics
482/// (if evidenced by high-enough performance improvement).
483#[allow(non_camel_case_types)]
484pub(crate) struct u64x8([u64; 8]);
485
486impl u64x8 {
487    /// Construct a `u64x8` from an array of 8 `u64`s
488    #[inline(always)]
489    pub(crate) fn from_array(a: [u64; 8]) -> Self {
490        Self(a)
491    }
492
493    /// Construct a `u64x8` from a pointer to 8 `u32`s
494    #[inline(always)]
495    pub(crate) unsafe fn from_u32_ptr(ptr: *const u32) -> Self {
496        // SAFETY: The caller must ensure the pointer is valid for reading 8 u32 values.
497        unsafe {
498            Self([
499                *ptr.add(0) as u64,
500                *ptr.add(1) as u64,
501                *ptr.add(2) as u64,
502                *ptr.add(3) as u64,
503                *ptr.add(4) as u64,
504                *ptr.add(5) as u64,
505                *ptr.add(6) as u64,
506                *ptr.add(7) as u64,
507            ])
508        }
509    }
510
511    /// Shift each element to the left by a given constant value, assigning the result to `self`
512    #[inline(always)]
513    pub(crate) fn shift_elements_left<const OFFSET: u32>(&mut self) {
514        for a in &mut self.0 {
515            *a <<= OFFSET;
516        }
517    }
518
519    /// Add a given value to each element, returning a new u64x8 with the result
520    #[inline(always)]
521    pub(crate) fn add_u64(&self, value: u64) -> Self {
522        Self::from_array([
523            self.0[0] + value,
524            self.0[1] + value,
525            self.0[2] + value,
526            self.0[3] + value,
527            self.0[4] + value,
528            self.0[5] + value,
529            self.0[6] + value,
530            self.0[7] + value,
531        ])
532    }
533
534    /// Write 8 contiguous elements starting from `ptr`
535    #[inline(always)]
536    pub(crate) unsafe fn store(&self, ptr: *mut u64) {
537        // SAFETY: The caller must ensure the pointer is valid for writing 8 u64 values.
538        unsafe {
539            *ptr.add(0) = self.0[0];
540            *ptr.add(1) = self.0[1];
541            *ptr.add(2) = self.0[2];
542            *ptr.add(3) = self.0[3];
543            *ptr.add(4) = self.0[4];
544            *ptr.add(5) = self.0[5];
545            *ptr.add(6) = self.0[6];
546            *ptr.add(7) = self.0[7];
547        }
548    }
549}
550
551/// Round a value up to the next multiple of the given power-of-two
552const fn next_multiple_of_pow2(val: usize, pow2: usize) -> usize {
553    debug_assert!(pow2.is_power_of_two());
554    (val + pow2 - 1) & !(pow2 - 1)
555}
556
557impl MainRef {
558    /// Get pointers to buffers for the given buffer indices, writing them into the provided `to` arrayvec.
559    ///
560    /// This is similar to `vlib_get_buffers` in the C API.
561    ///
562    /// Note that although it would be more idiomatic to return an `ArrayVec` directly, this
563    /// method takes a mutable reference to an `ArrayVec` to avoid an unnecessary copy when
564    /// returning.
565    ///
566    /// # Safety
567    ///
568    /// - The caller must ensure that `to` has enough capacity to hold all the buffers
569    ///   corresponding to the indices in `from_indices`.
570    /// - Each index in `from_indices` must be valid and the caller must have ownership of the
571    ///   buffer it corresponds to.
572    /// - Each buffer's `feature_arc_index` and `current_config_index` must be consistent with
573    ///   the `FeatureData` type. If they are not known (i.e. because the caller the node isn't
574    ///   being executed in a feature arc), FeatureData should be a zero-sized type such as `()`.
575    /// - The capacity of `from_indices` must be a multiple of 8 (note though that the length is
576    ///   allowed not to be). In other words, it must be valid to read multiples of 8 from the
577    ///   underlying memory (possibly returning uninitialised or stale data) without faulting.
578    #[inline(always)]
579    pub unsafe fn get_buffers<'a, 'me, 'buf: 'me, FeatureData, const N: usize>(
580        &'me self,
581        from_indices: &'a [u32],
582        to: &mut ArrayVec<&'buf mut BufferRef<FeatureData>, N>,
583    ) {
584        // SAFETY: The safety requirements are documented in the function's safety comment.
585        unsafe {
586            debug_assert!(from_indices.len() <= N);
587            assert_unchecked(from_indices.len() <= N);
588            // The vector operations below compute a pointer in terms of u64, i.e. assume that u64
589            // is a pointer-sized type the same as usize.
590            const_assert!(std::mem::size_of::<usize>() == std::mem::size_of::<u64>());
591
592            #[cfg(debug_assertions)]
593            for from_index in from_indices {
594                let buffer_mem_size = (*(*self.as_ptr()).buffer_main).buffer_mem_size;
595                debug_assert!(
596                    ((*from_index << CLIB_LOG2_CACHE_LINE_BYTES) as u64) < buffer_mem_size
597                );
598            }
599
600            let buffer_mem_start = (*(*self.as_ptr()).buffer_main).buffer_mem_start;
601
602            // Check for the ArrayVec capacity being a multiple of 8 and if so the later
603            // implementation can perform a write of 8 elements at a time without worrying about
604            // writing beyond the end of the ArrayVec. If not, then fall back to a generic
605            // implementation. This check will be evaluated at compile time and one implementation
606            // or the other chosen.
607            if !N.is_multiple_of(8) {
608                let base = buffer_mem_start as *const i8;
609                for from_index in from_indices.iter() {
610                    let ptr = base.add((*from_index << CLIB_LOG2_CACHE_LINE_BYTES) as usize)
611                        as *mut vlib_buffer_t;
612                    to.push_unchecked(BufferRef::from_ptr_mut(ptr));
613                }
614                return;
615            }
616
617            let mut len = from_indices.len();
618            len = next_multiple_of_pow2(len, 8);
619
620            let mut from_index = from_indices.as_ptr();
621            let mut to_ptr = to.as_mut_ptr();
622
623            while len >= 64 {
624                let mut from_index_x8_1 = u64x8::from_u32_ptr(from_index);
625                let mut from_index_x8_2 = u64x8::from_u32_ptr(from_index.add(8));
626                let mut from_index_x8_3 = u64x8::from_u32_ptr(from_index.add(2 * 8));
627                let mut from_index_x8_4 = u64x8::from_u32_ptr(from_index.add(3 * 8));
628                let mut from_index_x8_5 = u64x8::from_u32_ptr(from_index.add(4 * 8));
629                let mut from_index_x8_6 = u64x8::from_u32_ptr(from_index.add(5 * 8));
630                let mut from_index_x8_7 = u64x8::from_u32_ptr(from_index.add(6 * 8));
631                let mut from_index_x8_8 = u64x8::from_u32_ptr(from_index.add(7 * 8));
632
633                from_index_x8_1.shift_elements_left::<CLIB_LOG2_CACHE_LINE_BYTES>();
634                from_index_x8_2.shift_elements_left::<CLIB_LOG2_CACHE_LINE_BYTES>();
635                from_index_x8_3.shift_elements_left::<CLIB_LOG2_CACHE_LINE_BYTES>();
636                from_index_x8_4.shift_elements_left::<CLIB_LOG2_CACHE_LINE_BYTES>();
637                from_index_x8_5.shift_elements_left::<CLIB_LOG2_CACHE_LINE_BYTES>();
638                from_index_x8_6.shift_elements_left::<CLIB_LOG2_CACHE_LINE_BYTES>();
639                from_index_x8_7.shift_elements_left::<CLIB_LOG2_CACHE_LINE_BYTES>();
640                from_index_x8_8.shift_elements_left::<CLIB_LOG2_CACHE_LINE_BYTES>();
641
642                let buf_ptr_x8_1 = from_index_x8_1.add_u64(buffer_mem_start);
643                let buf_ptr_x8_2 = from_index_x8_2.add_u64(buffer_mem_start);
644                let buf_ptr_x8_3 = from_index_x8_3.add_u64(buffer_mem_start);
645                let buf_ptr_x8_4 = from_index_x8_4.add_u64(buffer_mem_start);
646                let buf_ptr_x8_5 = from_index_x8_5.add_u64(buffer_mem_start);
647                let buf_ptr_x8_6 = from_index_x8_6.add_u64(buffer_mem_start);
648                let buf_ptr_x8_7 = from_index_x8_7.add_u64(buffer_mem_start);
649                let buf_ptr_x8_8 = from_index_x8_8.add_u64(buffer_mem_start);
650
651                buf_ptr_x8_1.store(to_ptr as *mut u64);
652                buf_ptr_x8_2.store(to_ptr.add(8) as *mut u64);
653                buf_ptr_x8_3.store(to_ptr.add(2 * 8) as *mut u64);
654                buf_ptr_x8_4.store(to_ptr.add(3 * 8) as *mut u64);
655                buf_ptr_x8_5.store(to_ptr.add(4 * 8) as *mut u64);
656                buf_ptr_x8_6.store(to_ptr.add(5 * 8) as *mut u64);
657                buf_ptr_x8_7.store(to_ptr.add(6 * 8) as *mut u64);
658                buf_ptr_x8_8.store(to_ptr.add(7 * 8) as *mut u64);
659
660                to_ptr = to_ptr.add(64);
661                from_index = from_index.add(64);
662                len -= 64;
663            }
664
665            if likely(len >= 32) {
666                let mut from_index_x8_1 = u64x8::from_u32_ptr(from_index);
667                let mut from_index_x8_2 = u64x8::from_u32_ptr(from_index.add(8));
668                let mut from_index_x8_3 = u64x8::from_u32_ptr(from_index.add(2 * 8));
669                let mut from_index_x8_4 = u64x8::from_u32_ptr(from_index.add(3 * 8));
670
671                from_index_x8_1.shift_elements_left::<CLIB_LOG2_CACHE_LINE_BYTES>();
672                from_index_x8_2.shift_elements_left::<CLIB_LOG2_CACHE_LINE_BYTES>();
673                from_index_x8_3.shift_elements_left::<CLIB_LOG2_CACHE_LINE_BYTES>();
674                from_index_x8_4.shift_elements_left::<CLIB_LOG2_CACHE_LINE_BYTES>();
675
676                let buf_ptr_x8_1 = from_index_x8_1.add_u64(buffer_mem_start);
677                let buf_ptr_x8_2 = from_index_x8_2.add_u64(buffer_mem_start);
678                let buf_ptr_x8_3 = from_index_x8_3.add_u64(buffer_mem_start);
679                let buf_ptr_x8_4 = from_index_x8_4.add_u64(buffer_mem_start);
680
681                buf_ptr_x8_1.store(to_ptr as *mut u64);
682                buf_ptr_x8_2.store(to_ptr.add(8) as *mut u64);
683                buf_ptr_x8_3.store(to_ptr.add(2 * 8) as *mut u64);
684                buf_ptr_x8_4.store(to_ptr.add(3 * 8) as *mut u64);
685
686                to_ptr = to_ptr.add(32);
687                from_index = from_index.add(32);
688                len -= 32;
689            }
690
691            if likely(len >= 16) {
692                let mut from_index_x8_1 = u64x8::from_u32_ptr(from_index);
693                let mut from_index_x8_2 = u64x8::from_u32_ptr(from_index.add(8));
694
695                from_index_x8_1.shift_elements_left::<CLIB_LOG2_CACHE_LINE_BYTES>();
696                from_index_x8_2.shift_elements_left::<CLIB_LOG2_CACHE_LINE_BYTES>();
697
698                let buf_ptr_x8_1 = from_index_x8_1.add_u64(buffer_mem_start);
699                let buf_ptr_x8_2 = from_index_x8_2.add_u64(buffer_mem_start);
700
701                buf_ptr_x8_1.store(to_ptr as *mut u64);
702                buf_ptr_x8_2.store(to_ptr.add(8) as *mut u64);
703
704                to_ptr = to_ptr.add(16);
705                from_index = from_index.add(16);
706                len -= 16;
707            }
708
709            if likely(len > 0) {
710                let mut from_index_x8 = u64x8::from_u32_ptr(from_index);
711                from_index_x8.shift_elements_left::<CLIB_LOG2_CACHE_LINE_BYTES>();
712                let buf_ptr_x8 = from_index_x8.add_u64(buffer_mem_start);
713                buf_ptr_x8.store(to_ptr as *mut u64);
714            }
715
716            to.set_len(from_indices.len());
717        }
718    }
719
720    /// Enqueues a slice of buffer indices to a next node
721    ///
722    /// This corresponds to the VPP C function `vlib_buffer_enqueue_to_next`.
723    ///
724    /// # Safety
725    ///
726    /// - The length of the from and next slices must match.
727    /// - The next node must have a `Vector` type of `u32` (or the C equivalent).
728    /// - The next node must have a `Scalar` type of `()` (or the C equivalent).
729    /// - The next node must have an `Aux` type of `()` (or the C equivalent).
730    /// - `vlib_buffer_func_main` must have been filled in with valid function pointers (which
731    ///   will be done by VPP at initialisation time).
732    /// - The buffer state, such as `current_data` and `length` must be set according to the
733    ///   preconditions of the next node.
734    /// - Each entry in the `from` slice must be a valid index to a buffer.
735    /// - Each entry in the `nexts` slice must be a valid next node index.
736    #[inline(always)]
737    pub unsafe fn buffer_enqueue_to_next<N, V: VectorBufferIndex>(
738        &self,
739        node: &mut NodeRuntimeRef<N>,
740        from: &[V],
741        nexts: &[u16],
742    ) {
743        debug_assert_eq!(from.len(), nexts.len());
744        // SAFETY: the caller asserts the function preconditions are true
745        unsafe {
746            (vlib_buffer_func_main
747                .buffer_enqueue_to_next_fn
748                .unwrap_unchecked())(
749                self.as_ptr(),
750                node.as_ptr(),
751                VectorBufferIndex::as_u32_slice(from).as_ptr().cast_mut(),
752                nexts.as_ptr() as *mut u16,
753                from.len() as u64,
754            )
755        }
756    }
757
758    /// Allocate a single buffer
759    ///
760    /// This corresponds to the VPP C API of `vlib_alloc_buffers`.
761    pub fn alloc_buffer(&self) -> Result<BufferWithContext<'_>, BufferAllocError> {
762        // SAFETY: we have a reference to self so the pointer must also be valid, we pass in a
763        // buffer pointer that is consistent with the number of buffers asked for, and on exit
764        // of the function either the buffer value is filled in with a valid index we have
765        // ownership of or not depending on the return value of the function.
766        unsafe {
767            let mut buffer = 0;
768            let res = vlib_helper_buffer_alloc(self.as_ptr(), &mut buffer, 1);
769            if res == 1 {
770                Ok(BufferWithContext::from_parts(buffer.into(), self))
771            } else {
772                Err(BufferAllocError)
773            }
774        }
775    }
776
777    /// Get the default data size for allocated buffers
778    ///
779    /// This corresponds to the VPP C API `vlib_buffer_get_default_data_size`.
780    pub fn buffer_default_data_size(&self) -> u32 {
781        // SAFETY: we have a reference to self so the pointer must also be valid, and MainRef
782        // creation preconditions mean that the buffer_main point must also be valid.
783        unsafe { (*(*self.as_ptr()).buffer_main).default_data_size }
784    }
785}
786
787#[cfg(test)]
788mod tests {
789    use arrayvec::ArrayVec;
790
791    use crate::{
792        bindings::{CLIB_LOG2_CACHE_LINE_BYTES, vlib_buffer_main_t, vlib_buffer_t, vlib_main_t},
793        vlib::{MainRef, node::FRAME_SIZE},
794    };
795
796    #[test]
797    fn get_buffers() {
798        let buffer = vlib_buffer_t::default();
799        // This is picked deliberately to not be 128 - 8 - 1 to be the worst case in terms of maximising the code
800        // paths that need to be taken
801        const BUFFERS_N: usize = 119;
802        let buffers = [buffer; BUFFERS_N];
803        let buffer_indices: ArrayVec<u32, 128> = (0..buffers.len() as u32)
804            .map(|n| {
805                n * (std::mem::size_of::<vlib_buffer_t>() as u32 >> CLIB_LOG2_CACHE_LINE_BYTES)
806            })
807            .collect();
808        let mut buffer_main = vlib_buffer_main_t {
809            buffer_mem_start: std::ptr::addr_of!(buffers) as u64,
810            buffer_mem_size: std::mem::size_of_val(&buffers) as u64,
811            ..vlib_buffer_main_t::default()
812        };
813        let mut main = vlib_main_t {
814            buffer_main: std::ptr::addr_of_mut!(buffer_main),
815            ..vlib_main_t::default()
816        };
817        // SAFETY: pointers used by MainRef::get_buffers are initialised correctly and valid for
818        // the duration of the call.
819        unsafe {
820            let mut to = ArrayVec::new();
821            let main_ref = MainRef::from_ptr_mut(std::ptr::addr_of_mut!(main));
822            main_ref.get_buffers::<(), FRAME_SIZE>(&buffer_indices, &mut to);
823            assert_eq!(to.len(), BUFFERS_N);
824            for (i, buf_ref) in to.iter().enumerate() {
825                assert!(
826                    buf_ref.as_ptr().cast_const() == std::ptr::addr_of!(buffers[i]),
827                    "Buffer index {i} pointers don't match: {:p} expected {:p}",
828                    buf_ref.as_ptr(),
829                    std::ptr::addr_of!(buffers[i])
830                );
831            }
832        }
833    }
834}