Skip to main content

rumtk_core/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 rumtk_arena::dune::Dune;
24use rumtk_arena::rumtk_dune_new;
25use std::cmp::PartialEq;
26use std::mem;
27use std::ops::DerefMut;
28use std::ops::{Deref, RangeTo, RangeToInclusive};
29use std::ops::{Index, Range, RangeFull};
30use std::sync::LazyLock;
31
32#[derive(Default, Debug, Clone)]
33enum RUMBufferDataPtr {
34    StaticBuffer(&'static [u8]),
35    String(String),
36    Vec(RUMVec<u8>),
37    Arena(Dune),
38    #[default]
39    None
40}
41
42impl PartialEq for RUMBufferDataPtr {
43    fn eq(&self, other: &Self) -> bool {
44        mem::discriminant(self) == mem::discriminant(other)
45    }
46}
47
48pub type RUMBufferInner = RUMBufferDataPtr;
49
50const EMPTY_BUFFER_DATA: [u8;0] = [0;0];
51static EMPTY_RUMBUFFER: LazyLock<RUMBuffer> = LazyLock::new(|| RUMBuffer::new());
52
53///
54/// The [RUMBuffer] type is meant to be a very lightweight owned buffer pointer. The impetus for building
55/// this type is that benchmarking showed a potential vector getting allocated by the **bytes** crate.
56/// I was using the bytes crate before because it is a very solid crate. However, the vector allocations worried me.
57/// It was likely part of that crates interior mutability strategy with vtables but it was one of a few
58/// areas consuming a lot of time and most importantly consuming kernel time by invoking malloc.
59///
60/// ## Example
61/// ### Creation
62/// ```
63/// use rumtk_core::buffers::*;
64///
65/// let buffer = RUMBuffer::from("Hello World!");
66/// let expected = b"Hello World!";
67///
68/// assert_eq!(&buffer[..], expected, "Could not create RUMBuffer!");
69/// ```
70///
71/// ### Split Buffer
72/// ```
73/// use rumtk_core::buffers::*;
74///
75/// let mut buffer = RUMBuffer::from("Hello World!");
76/// let section = buffer.split_to(5);
77/// let expected = b"Hello";
78///
79/// assert_eq!(&section[..], expected, "Could not create RUMBuffer!");
80/// ```
81///
82#[derive(Debug)]
83pub struct RUMBuffer {
84    data: RUMBufferInner,
85    ptr: *const u8,
86    size: usize,
87}
88
89impl RUMBuffer {
90    #[inline]
91    pub const fn new() -> Self {
92        let data_length = 0;
93        let ptr = EMPTY_BUFFER_DATA.as_ptr();
94        Self {
95            data: RUMBufferDataPtr::None,
96            ptr,
97            size: data_length,
98        }
99    }
100
101    #[inline]
102    pub fn from_slice(data: &[u8]) -> Self {
103        if data.is_empty() {
104            return Self::new();
105        }
106
107        let data_length = data.len();
108        let mut mem = rumtk_dune_new!(data_length);
109        let mut ptr = mem.allocate_raw(data_length).unwrap();
110        let dst = as_slice_mut(ptr, data_length);
111        copy_from_slice(&data[..], dst);
112        Self {
113            data: RUMBufferDataPtr::Arena(mem),
114            ptr,
115            size: data_length,
116        }
117    }
118
119    #[inline]
120    pub fn from_vec(mut data: Vec<u8>) -> Self {
121        let mut ptr = data.as_mut_ptr();
122        let size = data.len();
123        Self {
124            data: RUMBufferDataPtr::Vec(data),
125            ptr,
126            size,
127        }
128    }
129
130    #[inline]
131    pub fn from_string(mut data: String) -> Self {
132        let mut ptr = data.as_mut_ptr();
133        let size = data.len();
134        Self {
135            data: RUMBufferDataPtr::String(data),
136            ptr,
137            size,
138        }
139    }
140
141    #[inline]
142    pub fn from_static(mut data: &'static [u8]) -> Self {
143        let mut ptr = data.as_mut_ptr();
144        let size = data.len();
145        Self {
146            data: RUMBufferDataPtr::StaticBuffer(data),
147            ptr,
148            size,
149        }
150    }
151
152    #[inline]
153    pub fn split_to(&mut self, offset: usize) -> Self {
154        debug_assert!(offset <= self.size, "offset too large");
155        let copy = Self {
156            data: RUMBufferDataPtr::None,
157            ptr: self.ptr,
158            size: offset,
159        };
160        self.ptr = unsafe { self.ptr.add(offset) };
161        self.size -= offset;
162        copy
163    }
164
165    #[inline]
166    pub fn freeze(&self) -> Self {
167        Self {
168            data: RUMBufferDataPtr::None,
169            ptr: self.ptr,
170            size: self.size,
171        }
172    }
173
174    #[inline]
175    pub fn to_vec(&self) -> RUMVec<u8> {
176        self.as_slice().to_vec()
177    }
178
179    #[inline]
180    pub fn as_str(&self) -> RUMResult<&str> {
181        buffer_to_str(self)
182    }
183
184    #[inline]
185    pub fn truncate(&mut self, len: usize) {
186        self.size = len;
187    }
188
189    #[inline]
190    pub fn is_empty(&self) -> bool {
191        self.size == 0
192    }
193
194    #[inline]
195    pub fn is_buffer(&self) -> bool {
196        !self.is_view()
197    }
198
199    #[inline]
200    pub fn is_view(&self) -> bool {
201        self.data == RUMBufferDataPtr::None
202    }
203}
204
205impl Iterator for RUMBuffer {
206    type Item = u8;
207
208    #[inline]
209    fn next(&mut self) -> Option<Self::Item> {
210        if self.size == 0 { return None };
211
212        let v = self[0];
213        self.ptr = unsafe { self.ptr.add(1) };
214        Some(v)
215    }
216}
217
218impl SizedType for RUMBuffer {
219    fn size(&self) -> usize {
220        self.size
221    }
222}
223
224impl AsPtr for RUMBuffer {
225    #[inline(always)]
226    fn as_ptr(&self) -> *const u8 {
227        self.ptr
228    }
229    #[inline(always)]
230    fn as_mut_ptr(&mut self) -> *mut u8 {
231        self.ptr as *mut u8
232    }
233}
234
235impl AsSlice for RUMBuffer {  }
236
237impl AsRef<[u8]> for RUMBuffer {
238    #[inline]
239    fn as_ref(&self) -> &[u8] {
240        self.as_slice()
241    }
242}
243
244impl Deref for RUMBuffer {
245    type Target = [u8];
246    #[inline]
247    fn deref(&self) -> &Self::Target {
248        self.as_slice()
249    }
250}
251
252impl DerefMut for RUMBuffer {
253    #[inline]
254    fn deref_mut(&mut self) -> &mut [u8] {
255        self.as_slice_mut()
256    }
257}
258
259impl PartialEq for RUMBuffer {
260    #[inline]
261    fn eq(&self, other: &Self) -> bool {
262        self.as_slice() == other.as_slice()
263    }
264}
265
266impl Default for RUMBuffer {
267    #[inline]
268    fn default() -> Self {
269        Self::new()
270    }
271}
272
273impl Clone for RUMBuffer {
274    #[inline]
275    fn clone(&self) -> Self {
276        self.freeze()
277    }
278}
279
280/*
281impl Drop for RUMBuffer {
282    fn drop(&mut self) {
283        match self.data.clone() {
284            Some(mem) => {
285                drop(mem)
286            },
287            None => {}
288        }
289    }
290}
291*/
292unsafe impl Send for RUMBuffer {}
293unsafe impl Sync for RUMBuffer {}
294
295///////////////////// Indexing ////////////////////////////////////
296
297impl Index<usize> for RUMBuffer {
298    type Output = u8;
299    #[inline]
300    fn index(&self, i: usize) -> &Self::Output {
301        &self.as_slice()[i]
302    }
303}
304
305impl Index<Range<usize>> for RUMBuffer {
306    type Output = [u8];
307    #[inline]
308    fn index(&self, i: Range<usize>) -> &Self::Output {
309        &self.as_slice()[i.start..i.end]
310    }
311}
312
313impl Index<RangeTo<usize>> for RUMBuffer {
314    type Output = [u8];
315    #[inline]
316    fn index(&self, i: RangeTo<usize>) -> &Self::Output {
317        &self.as_slice()[..i.end]
318    }
319}
320
321impl Index<RangeToInclusive<usize>> for RUMBuffer {
322    type Output = [u8];
323    #[inline]
324    fn index(&self, i: RangeToInclusive<usize>) -> &Self::Output {
325        &self.as_slice()[..=i.end]
326    }
327}
328
329impl Index<RangeFull> for RUMBuffer {
330    type Output = [u8];
331    #[inline]
332    fn index(&self, i: RangeFull) -> &Self::Output {
333        self.as_slice()
334    }
335}
336
337/////////////////////// Conversions ////////////////////////////////
338impl From<String> for RUMBuffer {
339    #[inline]
340    fn from(data: String) -> Self {
341        Self::from_string(data)
342    }
343}
344impl From<Vec<u8>> for RUMBuffer {
345    #[inline]
346    fn from(data: Vec<u8>) -> Self {
347        Self::from_vec(data)
348    }
349}
350
351impl From<&RUMVec<u8>> for RUMBuffer {
352    #[inline]
353    fn from(data: &RUMVec<u8>) -> Self {
354        Self::from_slice(data.as_slice())
355    }
356}
357
358impl From<&[u8]> for RUMBuffer {
359    #[inline]
360    fn from(data: &[u8]) -> Self {
361        Self::from_slice(data)
362    }
363}
364
365impl<const N: usize> From<&[u8; N]> for RUMBuffer {
366    #[inline]
367    fn from(data: &[u8; N]) -> Self {
368        Self::from(data.as_slice())
369    }
370}
371
372impl From<&str> for RUMBuffer {
373    #[inline]
374    fn from(data: &str) -> Self {
375        Self::from(data.as_bytes())
376    }
377}