Skip to main content

rumtk_arena/buffers/
rumbuffer.rs

1/*
2 *     rumtk attempts to implement HL7 and medical protocols for interoperability in medicine.
3 *     This toolkit aims to be reliable, simple, performant, and standards compliant.
4 *     Copyright (C) 2026  Luis M. Santos, M.D. <lsantos@medicalmasses.com>
5 *     Copyright (C) 2026  MedicalMasses L.L.C. <contact@medicalmasses.com>
6 *
7 *     This program is free software: you can redistribute it and/or modify
8 *     it under the terms of the GNU General Public License as published by
9 *     the Free Software Foundation, either version 3 of the License, or
10 *     (at your option) any later version.
11 *
12 *     This program is distributed in the hope that it will be useful,
13 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
14 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 *     GNU General Public License for more details.
16 *
17 *     You should have received a copy of the GNU General Public License
18 *     along with this program.  If not, see <https://www.gnu.org/licenses/>.
19 */
20use crate::base::{RUMResult, RUMVec};
21use crate::buffers::buffer_to_str;
22use crate::mem::{as_slice_mut, copy_from_slice, AsPtr, AsSlice, SizedType};
23use crate::rumtk_mem_layout;
24use std::alloc::{alloc, dealloc, Layout};
25use std::cmp::PartialEq;
26use std::ops::DerefMut;
27use std::ops::IndexMut;
28use std::ops::{Deref, RangeTo, RangeToInclusive};
29use std::ops::{Index, Range, RangeFull};
30use std::sync::LazyLock;
31
32const EMPTY_BUFFER_DATA: [u8;0] = [0;0];
33static EMPTY_RUMBUFFER: LazyLock<RUMBuffer> = LazyLock::new(|| RUMBuffer::new());
34
35///
36/// The [RUMBuffer] type is meant to be a very lightweight owned buffer pointer. The impetus for building
37/// this type is that benchmarking showed a potential vector getting allocated by the **bytes** crate.
38/// I was using the bytes crate before because it is a very solid crate. However, the vector allocations worried me.
39/// It was likely part of that crates interior mutability strategy with vtables but it was one of a few
40/// areas consuming a lot of time and most importantly consuming kernel time by invoking malloc.
41///
42/// ## Example
43/// ### Creation
44/// ```
45/// use rumtk_arena::buffers::*;
46///
47/// let buffer = RUMBuffer::from("Hello World!");
48/// let expected = b"Hello World!";
49///
50/// assert_eq!(&buffer[..], expected, "Could not create RUMBuffer!");
51/// ```
52///
53/// ### Split Buffer
54/// ```
55/// use rumtk_arena::buffers::*;
56///
57/// let mut buffer = RUMBuffer::from("Hello World!");
58/// let section = buffer.split_to(5).unwrap();
59/// let expected = b"Hello";
60///
61/// assert_eq!(&section[..], expected, "Could not create RUMBuffer!");
62/// ```
63///
64#[derive(Debug)]
65pub struct RUMBuffer {
66    data: *const u8,
67    size: u32,
68    dealloc: bool,
69}
70
71impl RUMBuffer {
72    #[inline]
73    pub const fn new() -> Self {
74        Self {
75            data: EMPTY_BUFFER_DATA.as_ptr(),
76            size: 0,
77            dealloc: false,
78        }
79    }
80
81    #[inline]
82    pub fn from_slice(data: &[u8]) -> Self {
83        if data.is_empty() {
84            return Self::new();
85        }
86
87        let data_length = data.len();
88        let ptr = unsafe { alloc(Layout::from_size_align_unchecked(data_length, size_of::<u8>())) };
89        let dst = as_slice_mut(ptr, data_length);
90        copy_from_slice(&data[..], dst);
91        Self {
92            data: ptr,
93            size: data_length as u32,
94            dealloc: true,
95        }
96    }
97
98    #[inline]
99    pub fn from_static(mut data: &'static [u8]) -> Self {
100        let ptr = data.as_ptr();
101        let size = data.len();
102        Self {
103            data: ptr,
104            size: size as u32,
105            dealloc: false,
106        }
107    }
108
109    #[inline]
110    pub fn from_parts(data: *const u8, length: usize, dealloc: bool) -> Self {
111        Self {
112            data,
113            size: length as u32,
114            dealloc,
115        }
116    }
117
118    #[inline]
119    pub fn split_to(&mut self, offset: usize) -> Option<Self> {
120        if offset <= self.size as usize {
121            let ptr = self.as_ptr();
122            let new_ptr = unsafe { ptr.add(offset) };
123            let copy = Self {
124                data: ptr,
125                size: offset as u32,
126                dealloc: false,
127            };
128            self.data = new_ptr;
129            self.size -= (offset as u32);
130            Some(copy)
131        } else {
132            None
133        }
134    }
135
136    #[inline]
137    pub fn freeze(&self) -> Self {
138        Self {
139            data: self.as_ptr(),
140            size: self.size,
141            dealloc: false,
142        }
143    }
144
145    #[inline]
146    pub fn to_vec(&self) -> RUMVec<u8> {
147        self.as_slice().to_vec()
148    }
149
150    #[inline]
151    pub fn as_str(&self) -> RUMResult<&str> {
152        buffer_to_str(self)
153    }
154
155    #[inline]
156    pub fn truncate(&mut self, len: usize) {
157        self.size = len as u32;
158    }
159
160    #[inline]
161    pub fn is_empty(&self) -> bool {
162        self.size == 0
163    }
164
165    #[inline]
166    pub fn is_buffer(&self) -> bool {
167        self.dealloc
168    }
169
170    #[inline]
171    pub fn is_view(&self) -> bool {
172        !self.dealloc
173    }
174}
175
176impl Iterator for RUMBuffer {
177    type Item = u8;
178
179    #[inline]
180    fn next(&mut self) -> Option<Self::Item> {
181        if self.size == 0 { return None };
182
183        let v = self[0];
184        self.data = unsafe { self.as_ptr().add(1) };
185        Some(v)
186    }
187}
188
189impl SizedType for RUMBuffer {
190    fn size(&self) -> usize {
191        self.size as usize
192    }
193}
194
195impl AsPtr for RUMBuffer {
196    #[inline(always)]
197    fn as_ptr(&self) -> *const u8 {
198        self.data
199    }
200    #[inline(always)]
201    fn as_mut_ptr(&mut self) -> *mut u8 {
202        self.data as *mut u8
203    }
204}
205
206impl AsSlice for RUMBuffer {  }
207
208impl AsRef<[u8]> for RUMBuffer {
209    #[inline]
210    fn as_ref(&self) -> &[u8] {
211        self.as_slice()
212    }
213}
214
215impl Deref for RUMBuffer {
216    type Target = [u8];
217    #[inline]
218    fn deref(&self) -> &Self::Target {
219        self.as_slice()
220    }
221}
222
223impl DerefMut for RUMBuffer {
224    #[inline]
225    fn deref_mut(&mut self) -> &mut [u8] {
226        self.as_slice_mut()
227    }
228}
229
230impl PartialEq for RUMBuffer {
231    #[inline]
232    fn eq(&self, other: &Self) -> bool {
233        self.as_slice() == other.as_slice()
234    }
235}
236
237impl Default for RUMBuffer {
238    #[inline]
239    fn default() -> Self {
240        Self::new()
241    }
242}
243
244impl Clone for RUMBuffer {
245    #[inline]
246    fn clone(&self) -> Self {
247        self.freeze()
248    }
249}
250
251impl Drop for RUMBuffer {
252    #[inline]
253    fn drop(&mut self) {
254        if self.dealloc {
255            unsafe {
256                dealloc(self.as_mut_ptr(), rumtk_mem_layout!(self.size as usize))
257            }
258        }
259    }
260}
261
262unsafe impl Send for RUMBuffer {}
263unsafe impl Sync for RUMBuffer {}
264
265///////////////////// Indexing ////////////////////////////////////
266
267impl Index<usize> for RUMBuffer {
268    type Output = u8;
269    #[inline]
270    fn index(&self, i: usize) -> &Self::Output {
271        &self.as_slice()[i]
272    }
273}
274
275impl Index<Range<usize>> for RUMBuffer {
276    type Output = [u8];
277    #[inline]
278    fn index(&self, i: Range<usize>) -> &Self::Output {
279        &self.as_slice()[i.start..i.end]
280    }
281}
282
283impl Index<RangeTo<usize>> for RUMBuffer {
284    type Output = [u8];
285    #[inline]
286    fn index(&self, i: RangeTo<usize>) -> &Self::Output {
287        &self.as_slice()[..i.end]
288    }
289}
290
291impl Index<RangeToInclusive<usize>> for RUMBuffer {
292    type Output = [u8];
293    #[inline]
294    fn index(&self, i: RangeToInclusive<usize>) -> &Self::Output {
295        &self.as_slice()[..=i.end]
296    }
297}
298
299impl Index<RangeFull> for RUMBuffer {
300    type Output = [u8];
301    #[inline]
302    fn index(&self, i: RangeFull) -> &Self::Output {
303        self.as_slice()
304    }
305}
306
307///////////////////// Indexing (Mut) ////////////////////////////////////
308
309impl IndexMut<usize> for RUMBuffer {
310    #[inline]
311    fn index_mut(&mut self, i: usize) -> &mut Self::Output {
312        &mut self.as_slice_mut()[i]
313    }
314}
315
316impl IndexMut<Range<usize>> for RUMBuffer {
317    #[inline]
318    fn index_mut(&mut self, i: Range<usize>) -> &mut Self::Output {
319        &mut self.as_slice_mut()[i.start..i.end]
320    }
321}
322
323impl IndexMut<RangeTo<usize>> for RUMBuffer {
324    #[inline]
325    fn index_mut(&mut self, i: RangeTo<usize>) -> &mut Self::Output {
326        &mut self.as_slice_mut()[..i.end]
327    }
328}
329
330impl IndexMut<RangeToInclusive<usize>> for RUMBuffer {
331    #[inline]
332    fn index_mut(&mut self, i: RangeToInclusive<usize>) -> &mut Self::Output {
333        &mut self.as_slice_mut()[..=i.end]
334    }
335}
336
337impl IndexMut<RangeFull> for RUMBuffer {
338    #[inline]
339    fn index_mut(&mut self, i: RangeFull) -> &mut Self::Output {
340        self.as_slice_mut()
341    }
342}
343
344/////////////////////// Conversions ////////////////////////////////
345impl From<String> for RUMBuffer {
346    #[inline]
347    fn from(mut data: String) -> Self {
348        let (s, l, c) = data.into_raw_parts();
349        Self::from_parts(s, l, true)
350    }
351}
352impl From<Vec<u8>> for RUMBuffer {
353    #[inline]
354    fn from(mut data: Vec<u8>) -> Self {
355        let (s, l, c) = data.into_raw_parts();
356        Self::from_parts(s, l, true)
357    }
358}
359
360impl From<&RUMVec<u8>> for RUMBuffer {
361    #[inline]
362    fn from(data: &RUMVec<u8>) -> Self {
363        Self::from_slice(data.as_slice())
364    }
365}
366
367impl From<&[u8]> for RUMBuffer {
368    #[inline]
369    fn from(data: &[u8]) -> Self {
370        Self::from_slice(data)
371    }
372}
373
374impl<const N: usize> From<&[u8; N]> for RUMBuffer {
375    #[inline]
376    fn from(data: &[u8; N]) -> Self {
377        Self::from(data.as_slice())
378    }
379}
380
381impl From<&str> for RUMBuffer {
382    #[inline]
383    fn from(data: &str) -> Self {
384        Self::from(data.as_bytes())
385    }
386}