Skip to main content

rumtk_arena/buffers/
helpers.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::as_slice_mut;
21use crate::base::*;
22use crate::buffers::RUMBuffer;
23use crate::cpu::*;
24use rand::{distr::Alphanumeric, RngExt};
25use std::alloc::{alloc, Layout};
26
27///
28/// Convert slice of `&[u8]` to [RUMBuffer].
29///
30/// ## Example
31/// ```
32/// use rumtk_arena::buffers::slice_to_buffer;
33/// use rumtk_arena::buffers::*;
34///
35/// const expected: &str = "Hello World!";
36/// let buffer = RUMBuffer::from(expected.as_bytes());
37/// let result = slice_to_buffer(expected.as_bytes());
38///
39/// assert_eq!(result, buffer, "Slice to RUMBuffer conversion failed!");
40/// ```
41///
42#[inline(always)]
43pub fn slice_to_buffer(buffer: &[u8]) -> RUMBuffer {
44    RUMBuffer::from(buffer)
45}
46
47///
48/// Generates a new random buffer using the `rand` crate and wrapped inside a [RUMBuffer](RUMBuffer).
49///
50/// The buffer size can be adjusted via the turbofish method => `new_random_buffer::<10>()`.
51///
52/// ## Example
53///
54/// ```
55/// use rumtk_arena::buffers::{new_random_buffer, DEFAULT_BUFFER_CHUNK_SIZE};
56///
57/// let buffer = new_random_buffer::<DEFAULT_BUFFER_CHUNK_SIZE>();
58///
59/// assert_eq!(buffer.is_empty(), false, "Function returned an empty random buffer which was unexpected!");
60/// assert_eq!(buffer.len(), DEFAULT_BUFFER_CHUNK_SIZE, "The new random buffer does not have the expected size!");
61/// ```
62///
63#[inline(always)]
64pub fn new_random_buffer<'a, const N: usize>() -> RUMVec<u8> {
65    let buffer = unsafe { alloc(Layout::from_size_align_unchecked(N, size_of::<u8>())) };
66    let slice = as_slice_mut(buffer, N * size_of::<u8>());
67
68    rand::fill(slice);
69    unsafe { RUMVec::from_raw_parts(buffer, N, N) }
70}
71
72///
73/// Generates a new random buffer using the `rand` crate and wrapped inside a [RUMBuffer](RUMBuffer).
74///
75/// The buffer size can be adjusted via the turbofish method => `new_random_buffer::<10>()`.
76///
77/// ## Example
78///
79/// ```
80/// use rumtk_arena::buffers::{new_random_buffer, DEFAULT_BUFFER_CHUNK_SIZE};
81///
82/// let buffer = new_random_buffer::<DEFAULT_BUFFER_CHUNK_SIZE>();
83///
84/// assert_eq!(buffer.is_empty(), false, "Function returned an empty random buffer which was unexpected!");
85/// assert_eq!(buffer.len(), DEFAULT_BUFFER_CHUNK_SIZE, "The new random buffer does not have the expected size!");
86/// ```
87///
88#[inline(always)]
89pub fn new_random_rumbuffer<const N: usize>() -> RUMBuffer {
90    slice_to_buffer(&new_random_buffer::<N>())
91}
92
93///
94/// Generates a new random string using the `rand` crate and wrapped inside a [RUMString](RUMString).
95///
96/// The buffer size can be adjusted via the turbofish method => `new_random_string_buffer::<10>()`.
97///
98/// ## Example
99///
100/// ```
101/// use rumtk_arena::buffers::{new_random_string_buffer, DEFAULT_BUFFER_CHUNK_SIZE};
102///
103/// let buffer = new_random_string_buffer::<DEFAULT_BUFFER_CHUNK_SIZE>();
104///
105/// assert_eq!(buffer.is_empty(), false, "Function returned an empty random buffer which was unexpected!");
106/// assert_eq!(buffer.len(), DEFAULT_BUFFER_CHUNK_SIZE, "The new random buffer does not have the expected size!");
107/// ```
108///
109pub fn new_random_string_buffer<const N: usize>() -> RUMString {
110    rand::rng()
111        .sample_iter(&Alphanumeric)
112        .take(N) // Length of the string
113        .map(char::from)
114        .collect()
115}
116
117///
118/// Generates a new random set of [RUMString] using the `rand` crate.
119///
120/// The buffer size for each item can be adjusted via the turbofish method => `new_random_string_set::<10>()`.
121///
122/// ## Example
123///
124/// ```
125/// use rumtk_arena::buffers::{new_random_string_set, DEFAULT_BUFFER_CHUNK_SIZE};
126///const item_count: usize = 5;
127///
128/// let buffer = new_random_string_set::<DEFAULT_BUFFER_CHUNK_SIZE>(item_count);
129///
130/// assert_eq!(buffer.is_empty(), false, "Function returned an empty random buffer which was unexpected!");
131/// assert_eq!(buffer.len(), item_count, "The new random buffer does not have the expected item count!");
132/// assert_eq!(buffer.get(0).unwrap().len(), DEFAULT_BUFFER_CHUNK_SIZE, "The new random buffer does not have the expected size!");
133/// ```
134///
135pub fn new_random_string_set<const N: usize>(item_count: usize) -> RUMVec<RUMString> {
136    let mut set = RUMVec::<RUMString>::with_capacity(item_count);
137
138    for _ in 0..item_count {
139        set.push(new_random_string_buffer::<N>())
140    }
141
142    set
143}
144
145///
146/// Convert buffer to string.
147///
148/// ## Example
149/// ```
150/// use rumtk_arena::buffers::buffer_to_string;
151/// use rumtk_arena::buffers::*;
152///
153/// const expected: &str = "Hello World!";
154/// let buffer = RUMBuffer::from(expected.as_bytes());
155/// let result = buffer_to_string(&buffer).unwrap();
156///
157/// assert_eq!(result, expected, "Buffer to RUMString conversion failed!");
158/// ```
159///
160#[inline(always)]
161pub fn buffer_to_string(buffer: &[u8]) -> RUMResult<RUMString> {
162    match RUMString::from_utf8(buffer.to_vec()) {
163        Ok(string) => Ok(string),
164        Err(e) => Err(rumtk_format!("Failure to parse incoming UTF-8 string: {}", e)),
165    }
166}
167
168#[inline(always)]
169pub fn buffer_to_str(buffer: &[u8]) -> RUMResult<&str> {
170    match std::str::from_utf8(buffer) {
171        Ok(string) => Ok(string),
172        Err(e) => Err(rumtk_format!("Failure to parse incoming UTF-8 string: {}", e)),
173    }
174}
175
176#[inline(always)]
177pub fn buffer_count(buffer: &[u8], pattern: u8) -> usize {
178    bytecount::count(buffer, pattern)
179}
180
181#[inline(always)]
182pub fn buffer_contains(buffer: &[u8], pattern: u8) -> bool {
183    buffer_find_byte(buffer, pattern).unwrap_or(buffer.len()) < buffer.len()
184}
185
186#[inline(always)]
187pub fn buffer_find_byte(buffer: &[u8], byte: u8) -> Option<usize> {
188    // Optimize the empty case
189    if buffer.is_empty() { return None }
190
191    // Optimize the byte is next door case
192    if buffer[0] == byte { return Some(0) }
193
194    // Attempt normal search otherwise.
195    cpu_find(buffer, byte)
196}
197
198#[inline(always)]
199pub fn buffer_find(buffer: &[u8], pattern: &[u8]) -> usize {
200    if buffer.is_empty() {
201        return buffer.len();
202    }
203
204    let start_pattern_byte = pattern[0];
205    let pattern_length = pattern.len();
206    let buffer_len = buffer.len();
207    let end = buffer_len - pattern_length;
208    let mut cursor = 0;
209
210    loop {
211        match buffer_find_byte(&buffer[cursor..], start_pattern_byte) {
212            Some(indx) => {
213                cursor += indx;
214                if cursor > end { return buffer_len; }
215
216                let slice = &buffer[cursor..cursor + pattern_length];
217                if  slice == pattern {
218                    return cursor;
219                }
220
221                cursor += 1;
222            },
223            None => return buffer_len,
224        }
225    }
226}
227
228#[inline(always)]
229pub fn buffer_find_instances<'a>(buffer: &'a [u8], pattern: &[u8]) -> RUMVec<(usize, &'a [u8])> {
230    if buffer.is_empty() {
231        return RUMVec::new();
232    }
233
234    let pattern_length = pattern.len();
235    let buffer_length = buffer.len() - pattern_length;
236    let mut instances = RUMVec::<(usize, &[u8])>::with_capacity(100);
237
238    let mut cursor = buffer_find(buffer, pattern);
239    let mut cumulative = cursor;
240    let mut remainder = &buffer[..];
241
242    while cumulative < buffer_length {
243        instances.push((cumulative, &remainder[..cursor]));
244        let next = cursor + pattern_length;
245        if next <= remainder.len() {
246            remainder = &remainder[cursor + pattern_length..];
247            cursor = buffer_find(remainder, pattern);
248            cumulative += cursor;
249        } else {
250            cumulative += remainder.len();
251        }
252    }
253
254    instances
255}
256
257#[inline(always)]
258pub fn buffer_replace_in_place<'a>(buffer: &'a mut [u8], pattern: &[u8], replacement: &[u8]) {
259    // Optimize the nothing passed to us case.
260    if buffer.is_empty() || pattern.is_empty() || replacement.is_empty() {
261        return;
262    }
263
264    // Optimize the single byte replace using SIMD
265    if pattern.len() == 1  {
266        cpu_replace_byte(buffer, pattern[0], replacement[0]);
267        return;
268    }
269
270    let replacement_length = replacement.len();
271    let mut cursor = buffer_find(&buffer, pattern);
272    let mut remainder = buffer;
273
274    while cursor < remainder.len() {
275        for i in 0..replacement_length {
276            remainder[cursor + i] = replacement[i];
277        }
278
279        remainder = &mut remainder[cursor + pattern.len()..];
280        cursor = buffer_find(remainder, pattern);
281    }
282}
283
284#[inline(always)]
285pub fn buffer_replace(buffer: &[u8], pattern: &[u8], replacement: &[u8]) -> RUMBuffer {
286    match buffer.is_empty() {
287        true => RUMBuffer::from(buffer),
288        false => {
289            let pattern_length = pattern.len();
290            let replacement_length = replacement.len();
291            let instances = buffer_find_instances(&buffer, pattern);
292            let mut new_buffer =  RUMVec::with_capacity(buffer.len() + (instances.len() * (replacement_length)));
293            let mut last = 0;
294
295            for (indx, chunk) in instances {
296                new_buffer.extend_from_slice(chunk);
297                new_buffer.extend_from_slice(replacement);
298                last = indx + pattern_length;
299            }
300
301            new_buffer.extend_from_slice(&buffer[last..]);
302            RUMBuffer::from(new_buffer)
303        }
304    }
305}
306
307#[inline]
308pub fn buffer_trim(buffer: &[u8]) -> RUMBuffer {
309    let trimmed = buffer_slice_trim(buffer);
310    RUMBuffer::from_parts(trimmed.as_ptr(), trimmed.len(), false)
311}
312
313#[inline(always)]
314pub fn buffer_slice_trim(buffer: &[u8]) -> &[u8] {
315    buffer.trim_ascii()
316}
317
318pub fn buffer_has_pattern(buffer: &[u8], pattern: &[u8]) -> bool {
319    buffer_find(buffer, pattern) != buffer.len()
320}
321
322pub fn is_unique_bytes(data: &[u8]) -> bool {
323    let mut items = ahash::AHashSet::with_capacity(data.len());
324    for i in 0..data.len() {
325        if !items.insert(data[i]) {
326            return false;
327        }
328    }
329    true
330}