Skip to main content

osal_rs/
utils.rs

1/***************************************************************************
2 *
3 * osal-rs
4 * Copyright (C) 2023/2026 Antonio Salsi <passy.linux@zresa.it>
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 *
18 ***************************************************************************/
19
20//! Utility types and functions for OSAL-RS.
21//!
22//! This module contains common types, error definitions, and helper functions
23//! used throughout the library.
24
25use core::cell::UnsafeCell;
26use core::ffi::{CStr, c_char, c_void};
27use core::str::{from_utf8_mut, FromStr};
28use core::fmt::{Debug, Display}; 
29use core::ops::{Deref, DerefMut};
30use core::time::Duration;
31
32use alloc::ffi::CString;
33use alloc::format;
34use alloc::string::{String, ToString};
35use alloc::vec::Vec;
36
37#[cfg(not(feature = "serde"))]
38use crate::os::{Deserialize, Serialize};
39
40#[cfg(feature = "serde")]
41use osal_rs_serde::{Deserialize, Serialize};
42
43use crate::os::AsSyncStr;
44
45/// Error types for OSAL-RS operations.
46///
47/// Represents all possible error conditions that can occur when using
48/// the OSAL-RS library.
49///
50/// # Lifetime Parameter
51///
52/// The error type is generic over lifetime `'a` to allow flexible error messages.
53/// Most of the time, you can use the default [`Result<T>`] type alias which uses
54/// `Error<'static>`. For custom lifetimes in error messages, use
55/// `core::result::Result<T, Error<'a>>` explicitly.
56///
57/// # Examples
58///
59/// ## Basic usage with static errors
60///
61/// ```ignore
62/// use osal_rs::os::{Queue, QueueFn};
63/// use osal_rs::utils::Error;
64/// 
65/// match Queue::new(10, 32) {
66///     Ok(queue) => { /* use queue */ },
67///     Err(Error::OutOfMemory) => println!("Failed to allocate queue"),
68///     Err(e) => println!("Other error: {:?}", e),
69/// }
70/// ```
71///
72/// ## Using borrowed error messages
73///
74/// ```ignore
75/// use osal_rs::utils::Error;
76/// 
77/// fn validate_input(input: &str) -> core::result::Result<(), Error> {
78///     if input.is_empty() {
79///         // Use static lifetime for compile-time strings
80///         Err(Error::Unhandled("Input cannot be empty"))
81///     } else {
82///         Ok(())
83///     }
84/// }
85/// 
86/// // For dynamic error messages from borrowed data
87/// fn process_data<'a>(data: &'a str) -> core::result::Result<(), Error<'a>> {
88///     if !data.starts_with("valid:") {
89///         // Error message borrows from 'data' lifetime
90///         Err(Error::ReadError(data))
91///     } else {
92///         Ok(())
93///     }
94/// }
95/// ```
96#[derive(Debug, Clone, PartialEq, Eq, Hash)]
97pub enum Error<'a> {
98    /// Insufficient memory to complete operation
99    OutOfMemory,
100    /// Queue send operation timed out
101    QueueSendTimeout,
102    /// Queue receive operation timed out
103    QueueReceiveTimeout,
104    /// Mutex operation timed out
105    MutexTimeout,
106    /// Failed to acquire mutex lock
107    MutexLockFailed,
108    /// Generic timeout error
109    Timeout,
110    /// Queue is full and cannot accept more items
111    QueueFull,
112    /// String conversion failed
113    StringConversionError,
114    /// Thread/task not found
115    TaskNotFound,
116    /// Invalid queue size specified
117    InvalidQueueSize,
118    /// Null pointer encountered
119    NullPtr,
120    /// Requested item not found
121    NotFound,
122    /// Index out of bounds
123    OutOfIndex,
124    /// Invalid type for operation
125    InvalidType,
126    /// No data available
127    Empty,
128    /// Write error occurred
129    WriteError(&'a str),
130    /// Read error occurred
131    ReadError(&'a str),
132    /// Return error with code
133    ReturnWithCode(i32),
134    /// Unhandled error with description
135    Unhandled(&'a str),
136    /// Unhandled error with description owned
137    UnhandledOwned(String)
138}
139
140impl<'a> Display for Error<'a> {
141    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
142        use Error::*;
143
144        match self {
145            OutOfMemory => write!(f, "Out of memory"),
146            QueueSendTimeout => write!(f, "Queue send timeout"),
147            QueueReceiveTimeout => write!(f, "Queue receive timeout"),
148            MutexTimeout => write!(f, "Mutex timeout"),
149            MutexLockFailed => write!(f, "Mutex lock failed"),
150            Timeout => write!(f, "Operation timeout"),
151            QueueFull => write!(f, "Queue full"),
152            StringConversionError => write!(f, "String conversion error"),
153            TaskNotFound => write!(f, "Task not found"),
154            InvalidQueueSize => write!(f, "Invalid queue size"),
155            NullPtr => write!(f, "Null pointer encountered"),
156            NotFound => write!(f, "Item not found"),
157            OutOfIndex => write!(f, "Index out of bounds"),
158            InvalidType => write!(f, "Invalid type for operation"),
159            Empty => write!(f, "No data available"),
160            WriteError(desc) => write!(f, "Write error occurred: {}", desc),
161            ReadError(desc) => write!(f, "Read error occurred: {}", desc),
162            ReturnWithCode(code) => write!(f, "Return with code: {}", code),
163            Unhandled(desc) => write!(f, "Unhandled error: {}", desc),
164            UnhandledOwned(desc) => write!(f, "Unhandled error owned: {}", desc),
165        }
166    }
167}
168
169
170/// CPU register size enumeration.
171///
172/// Identifies whether the target CPU uses 32-bit or 64-bit registers.
173/// This is used for platform-specific tick count overflow handling.
174#[derive(PartialEq, Eq, Clone, Copy, Debug)]
175pub enum CpuRegisterSize {
176    /// 64-bit CPU registers
177    Bit64,
178    /// 32-bit CPU registers
179    Bit32
180}
181
182/// Boolean type compatible with RTOS return values.
183///
184/// Many RTOS functions return 0 for success and non-zero for failure.
185/// This type provides a Rust-idiomatic way to work with such values.
186///
187/// # Examples
188///
189/// ```ignore
190/// use osal_rs::os::{Semaphore, SemaphoreFn};
191/// use osal_rs::utils::OsalRsBool;
192/// use core::time::Duration;
193/// 
194/// let sem = Semaphore::new(1, 1).unwrap();
195/// 
196/// match sem.wait(Duration::from_millis(100)) {
197///     OsalRsBool::True => println!("Acquired semaphore"),
198///     OsalRsBool::False => println!("Failed to acquire"),
199/// }
200/// 
201/// // Can also convert to bool
202/// if sem.signal().into() {
203///     println!("Semaphore signaled");
204/// }
205/// ```
206#[derive(PartialEq, Eq, Clone, Copy, Debug)]
207#[repr(u8)]
208pub enum OsalRsBool {
209    /// Operation failed or condition is false
210    False = 1,
211    /// Operation succeeded or condition is true
212    True = 0
213}
214
215/// Maximum delay constant for blocking operations.
216///
217/// When used as a timeout parameter, indicates the operation should
218/// block indefinitely until it succeeds.
219///
220/// # Examples
221///
222/// ```ignore
223/// use osal_rs::os::{Mutex, MutexFn};
224/// use osal_rs::utils::MAX_DELAY;
225/// 
226/// let mutex = Mutex::new(0);
227/// let guard = mutex.lock();  // Blocks forever if needed
228/// ```
229pub const MAX_DELAY: Duration = Duration::from_millis(usize::MAX as u64);
230
231/// Standard Result type for OSAL-RS operations.
232///
233/// Uses [`Error`] as the default error type with `'static` lifetime.
234/// For custom lifetimes, use `core::result::Result<T, Error<'a>>`.
235pub type Result<T, E = Error<'static>> = core::result::Result<T, E>;
236
237/// Pointer to pointer type for C FFI.
238pub type DoublePtr = *mut *mut c_void;
239
240/// Mutable pointer type for C FFI.
241pub type Ptr = *mut c_void;
242
243/// Const pointer type for C FFI.
244pub type ConstPtr = *const c_void;
245
246
247/// Determines the CPU register size at compile time.
248///
249/// This constant function checks the size of `usize` to determine whether
250/// the target architecture uses 32-bit or 64-bit registers. This information
251/// is used for platform-specific optimizations and overflow handling.
252///
253/// # Returns
254///
255/// * [`CpuRegisterSize::Bit64`] - For 64-bit architectures
256/// * [`CpuRegisterSize::Bit32`] - For 32-bit architectures
257///
258/// # Examples
259///
260/// ```ignore
261/// use osal_rs::utils::{register_bit_size, CpuRegisterSize};
262/// 
263/// match register_bit_size() {
264///     CpuRegisterSize::Bit64 => println!("Running on 64-bit platform"),
265///     CpuRegisterSize::Bit32 => println!("Running on 32-bit platform"),
266/// }
267/// ```
268pub const fn register_bit_size() -> CpuRegisterSize {
269    if size_of::<usize>() == 8 {
270        CpuRegisterSize::Bit64
271    } else {
272        CpuRegisterSize::Bit32
273    }
274}
275
276/// Converts a C string pointer to a Rust String.
277///
278/// This macro safely converts a raw C string pointer (`*const c_char`) into
279/// a Rust `String`. It handles UTF-8 conversion gracefully using lossy conversion.
280///
281/// # Safety
282///
283/// The pointer must be valid and point to a null-terminated C string.
284///
285/// # Examples
286///
287/// ```ignore
288/// use osal_rs::from_c_str;
289/// use core::ffi::c_char;
290/// 
291/// extern "C" {
292///     fn get_system_name() -> *const c_char;
293/// }
294/// 
295/// let name = from_c_str!(get_system_name());
296/// println!("System: {}", name);
297/// ```
298#[macro_export]
299macro_rules! from_c_str {
300    ($str:expr) => {
301        unsafe {
302            let c_str = core::ffi::CStr::from_ptr($str);
303            alloc::string::String::from_utf8_lossy(c_str.to_bytes()).to_string()
304        }
305    };
306}
307
308/// Converts a Rust string to a CString with error handling.
309///
310/// This macro creates a `CString` from a Rust string reference, returning
311/// a `Result` that can be used with the `?` operator. If the conversion fails
312/// (e.g., due to interior null bytes), it returns an appropriate error.
313///
314/// # Returns
315///
316/// * `Ok(CString)` - On successful conversion
317/// * `Err(Error::Unhandled)` - If the string contains interior null bytes
318///
319/// # Examples
320///
321/// ```ignore
322/// use osal_rs::to_cstring;
323/// use osal_rs::utils::Result;
324/// 
325/// fn pass_to_c_api(name: &str) -> Result<()> {
326///     let c_name = to_cstring!(name)?;
327///     // Use c_name.as_ptr() with C FFI
328///     Ok(())
329/// }
330/// ```
331#[macro_export]
332macro_rules! to_cstring {
333    ($s:expr) => {
334        alloc::ffi::CString::new($s.as_str())
335            .map_err(|_| $crate::utils::Error::Unhandled("Failed to convert string to CString"))
336    };
337}
338
339/// Converts a Rust string to a C string pointer.
340///
341/// This macro creates a `CString` from a Rust string and returns its raw pointer.
342/// **Warning**: This macro panics if the conversion fails. Consider using
343/// [`to_cstring!`] for safer error handling.
344///
345/// # Panics
346///
347/// Panics if the string contains interior null bytes.
348///
349/// # Examples
350///
351/// ```ignore
352/// use osal_rs::to_c_str;
353/// 
354/// extern "C" {
355///     fn set_name(name: *const core::ffi::c_char);
356/// }
357/// 
358/// let name = "FreeRTOS Task";
359/// unsafe {
360///     set_name(to_c_str!(name));
361/// }
362/// ```
363#[macro_export]
364macro_rules! to_c_str {
365    ($s:expr) => {
366        alloc::ffi::CString::new($s.as_ref() as &str).unwrap().as_ptr()
367    };
368}
369
370/// Converts a string to a fixed-size byte array.
371///
372/// This macro creates a byte array of the specified size and fills it with
373/// the bytes from the input string. If the string is shorter than the buffer,
374/// the remaining bytes are filled with spaces. If the string is longer, it
375/// is truncated to fit.
376///
377/// # Parameters
378///
379/// * `$str` - The source string to convert
380/// * `$buff_name` - The identifier name for the created buffer variable
381/// * `$buff_size` - The size of the byte array to create
382///
383/// # Examples
384///
385/// ```ignore
386/// use osal_rs::from_str_to_array;
387/// 
388/// let task_name = "MainTask";
389/// from_str_to_array!(task_name, name_buffer, 16);
390/// // name_buffer is now [u8; 16] containing "MainTask        "
391/// 
392/// // Use with C FFI
393/// extern "C" {
394///     fn create_task(name: *const u8, len: usize);
395/// }
396/// 
397/// unsafe {
398///     create_task(name_buffer.as_ptr(), name_buffer.len());
399/// }
400/// ```
401#[macro_export]
402macro_rules! from_str_to_array {
403    ($str:expr, $buff_name:ident, $buff_size:expr) => {
404        let mut $buff_name = [b' '; $buff_size];
405        let _bytes = $str.as_bytes();
406        let _len = core::cmp::min(_bytes.len(), $buff_size);
407        $buff_name[.._len].copy_from_slice(&_bytes[.._len]);
408    };
409}
410
411/// Extracts a typed parameter from an optional boxed Any reference.
412///
413/// This macro is used in thread/task entry points to safely extract and
414/// downcast parameters passed to the thread. It handles both the Option
415/// unwrapping and the type downcast, returning appropriate errors if either
416/// operation fails.
417///
418/// # Parameters
419///
420/// * `$param` - An `Option<Box<dyn Any>>` containing the parameter
421/// * `$t` - The type to downcast the parameter to
422///
423/// # Returns
424///
425/// * A reference to the downcasted value of type `$t`
426/// * `Err(Error::NullPtr)` - If the parameter is None
427/// * `Err(Error::InvalidType)` - If the downcast fails
428///
429/// # Examples
430///
431/// ```ignore
432/// use osal_rs::thread_extract_param;
433/// use osal_rs::utils::Result;
434/// use core::any::Any;
435/// 
436/// struct TaskConfig {
437///     priority: u8,
438///     stack_size: usize,
439/// }
440/// 
441/// fn task_entry(param: Option<Box<dyn Any>>) -> Result<()> {
442///     let config = thread_extract_param!(param, TaskConfig);
443///     
444///     println!("Priority: {}", config.priority);
445///     println!("Stack: {}", config.stack_size);
446///     
447///     Ok(())
448/// }
449/// ```
450#[macro_export]
451macro_rules! thread_extract_param {
452    ($param:expr, $t:ty) => {
453        match $param.as_ref() {
454            Some(p) => {
455                match p.downcast_ref::<$t>() {
456                    Some(value) => value,
457                    None => return Err($crate::utils::Error::InvalidType),
458                }
459            }
460            None => return Err($crate::utils::Error::NullPtr),
461        }
462    };
463}
464
465
466/// Fixed-size byte array wrapper with string conversion utilities.
467///
468/// `Bytes` is a generic wrapper around a fixed-size byte array that provides
469/// convenient methods for converting between strings and byte arrays. It's
470/// particularly useful for interfacing with C APIs that expect fixed-size
471/// character buffers, or for storing strings in embedded systems with
472/// constrained memory.
473///
474/// # Type Parameters
475///
476/// * `SIZE` - The size of the internal byte array (default: 0)
477///
478/// # Examples
479///
480/// ```ignore
481/// use osal_rs::utils::Bytes;
482/// 
483/// // Create an empty 32-byte buffer
484/// let mut buffer = Bytes::<32>::new();
485/// 
486/// // Create a buffer from a string
487/// let name = Bytes::<16>::new_by_str("TaskName");
488/// println!("{}", name); // Prints "TaskName"
489/// 
490/// // Create from any type that implements ToString
491/// let number = 42;
492/// let num_bytes = Bytes::<8>::new_by_string(&number);
493/// ```
494
495#[cfg(feature = "serde")]
496#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
497pub struct Bytes<const SIZE: usize> (pub [u8; SIZE]);
498
499#[cfg(not(feature = "serde"))]
500#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
501pub struct Bytes<const SIZE: usize> (pub [u8; SIZE]);
502
503impl<const SIZE: usize> Deref for Bytes<SIZE> {
504    type Target = [u8; SIZE];
505
506    /// Dereferences to the underlying byte array.
507    ///
508    /// This allows `Bytes` to be used anywhere a `[u8; SIZE]` reference is expected.
509    ///
510    /// # Examples
511    ///
512    /// ```ignore
513    /// use osal_rs::utils::Bytes;
514    /// 
515    /// let bytes = Bytes::<8>::new_by_str("test");
516    /// assert_eq!(bytes[0], b't');
517    /// ```
518    fn deref(&self) -> &Self::Target {
519        &self.0
520    }
521}
522
523impl<const SIZE: usize> DerefMut for Bytes<SIZE> {
524    /// Provides mutable access to the underlying byte array.
525    ///
526    /// This allows `Bytes` to be mutably dereferenced, enabling direct modification
527    /// of the internal byte array through the `DerefMut` trait.
528    ///
529    /// # Examples
530    ///
531    /// ```ignore
532    /// use osal_rs::utils::Bytes;
533    /// 
534    /// let mut bytes = Bytes::<8>::new();
535    /// bytes[0] = b'H';
536    /// bytes[1] = b'i';
537    /// assert_eq!(bytes[0], b'H');
538    /// ```
539    fn deref_mut(&mut self) -> &mut Self::Target {
540        &mut self.0
541    }
542}
543
544impl<const SIZE: usize> Display for Bytes<SIZE> {
545    /// Formats the byte array as a C-style null-terminated string.
546    ///
547    /// This implementation treats the byte array as a C string and converts it
548    /// to a Rust string for display. If the conversion fails, it displays
549    /// "Conversion error".
550    ///
551    /// # Safety
552    ///
553    /// This method assumes the byte array contains valid UTF-8 data and is
554    /// null-terminated. Invalid data may result in the error message being displayed.
555    ///
556    /// # Examples
557    ///
558    /// ```ignore
559    /// use osal_rs::utils::Bytes;
560    /// 
561    /// let bytes = Bytes::<16>::new_by_str("Hello");
562    /// println!("{}", bytes); // Prints "Hello"
563    /// ```
564    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
565        let str = unsafe {
566            CStr::from_ptr(self.0.as_ptr() as *const c_char)
567            .to_str()
568            .unwrap_or("Conversion error")
569        };
570        
571        write!(f, "{}", str.to_string())
572    }
573}
574
575impl<const SIZE: usize> FromStr for Bytes<{SIZE}> {
576    type Err = Error<'static>;
577
578    #[inline]
579    fn from_str(s: &str) -> core::result::Result<Self, Self::Err> {
580        Ok(Self::new_by_str(s))
581    }
582}
583
584impl<const SIZE: usize> AsSyncStr for Bytes<SIZE> {
585    /// Returns a string slice reference.
586    ///
587    /// This method provides access to the underlying string data in a way
588    /// that is safe to use across thread boundaries.
589    ///
590    /// # Returns
591    ///
592    /// A reference to a string slice with lifetime tied to `self`.
593    fn as_str(&self) -> &str {
594        unsafe {
595            CStr::from_ptr(self.0.as_ptr() as *const c_char)
596            .to_str()
597            .unwrap_or("Conversion error")
598        }
599    }
600}
601
602/// Serialization implementation for `Bytes<SIZE>` when the `serde` feature is enabled.
603///
604/// This implementation provides serialization by directly serializing each byte
605/// in the array using the osal-rs-serde serialization framework.
606#[cfg(feature = "serde")]
607impl<const SIZE: usize> Serialize for Bytes<SIZE> {
608    /// Serializes the `Bytes` instance using the given serializer.
609    ///
610    /// # Parameters
611    ///
612    /// * `serializer` - The serializer to use
613    ///
614    /// # Returns
615    ///
616    /// * `Ok(())` - On successful serialization
617    /// * `Err(S::Error)` - If serialization fails
618    fn serialize<S: osal_rs_serde::Serializer>(&self, name: &str, serializer: &mut S) -> core::result::Result<(), S::Error> {
619        // Find the actual length (up to first null byte or SIZE)
620        let len = self.0.iter().position(|&b| b == 0).unwrap_or(SIZE);
621        
622        // Try to serialize as UTF-8 string if valid, otherwise as hex
623        if let Ok(s) = core::str::from_utf8(&self.0[..len]) {
624            serializer.serialize_str(name, s)
625        } else {
626            // For binary data, serialize as bytes (hex encoded)
627            serializer.serialize_bytes(name, &self.0[..len])
628        }
629    }
630}
631
632/// Deserialization implementation for `Bytes<SIZE>` when the `serde` feature is enabled.
633///
634/// This implementation provides deserialization by reading bytes from the deserializer
635/// into a fixed-size array using the osal-rs-serde deserialization framework.
636#[cfg(feature = "serde")]
637impl<const SIZE: usize> Deserialize for Bytes<SIZE> {
638    /// Deserializes a `Bytes` instance using the given deserializer.
639    ///
640    /// # Parameters
641    ///
642    /// * `deserializer` - The deserializer to use
643    ///
644    /// # Returns
645    ///
646    /// * `Ok(Bytes<SIZE>)` - A new `Bytes` instance with deserialized data
647    /// * `Err(D::Error)` - If deserialization fails
648    fn deserialize<D: osal_rs_serde::Deserializer>(deserializer: &mut D, name: &str) -> core::result::Result<Self, D::Error> {
649        let mut array = [0u8; SIZE];
650        let _ = deserializer.deserialize_bytes(name, &mut array)?;
651        Ok(Self(array))
652    }
653}
654
655/// Serialization implementation for `Bytes<SIZE>` when the `serde` feature is disabled.
656///
657/// This implementation provides basic serialization by directly returning a reference
658/// to the underlying byte array. It's used when the library is compiled without the
659/// `serde` feature, providing a lightweight alternative serialization mechanism.
660#[cfg(not(feature = "serde"))]
661impl<const SIZE: usize> Serialize for Bytes<SIZE> {
662    /// Converts the `Bytes` instance to a byte slice.
663    ///
664    /// # Returns
665    ///
666    /// A reference to the internal byte array.
667    fn to_bytes(&self) -> &[u8] {
668        &self.0
669    }
670}
671
672/// Deserialization implementation for `Bytes<SIZE>` when the `serde` feature is disabled.
673///
674/// This implementation provides basic deserialization by copying bytes from a slice
675/// into a fixed-size array. If the source slice is shorter than `SIZE`, the remaining
676/// bytes are zero-filled. If longer, it's truncated to fit.
677#[cfg(not(feature = "serde"))]
678impl<const SIZE: usize> Deserialize for Bytes<SIZE> {
679    /// Creates a `Bytes` instance from a byte slice.
680    ///
681    /// # Parameters
682    ///
683    /// * `bytes` - The source byte slice to deserialize from
684    ///
685    /// # Returns
686    ///
687    /// * `Ok(Bytes<SIZE>)` - A new `Bytes` instance with data copied from the slice
688    ///
689    /// # Examples
690    ///
691    /// ```ignore
692    /// use osal_rs::utils::Bytes;
693    /// use osal_rs::os::Deserialize;
694    /// 
695    /// let data = b"Hello";
696    /// let bytes = Bytes::<16>::from_bytes(data).unwrap();
697    /// // Result: [b'H', b'e', b'l', b'l', b'o', 0, 0, 0, ...]
698    /// ```
699    fn from_bytes(bytes: &[u8]) -> Result<Self> {
700        let mut array = [0u8; SIZE];
701        let len = core::cmp::min(bytes.len(), SIZE);
702        array[..len].copy_from_slice(&bytes[..len]);
703        Ok(Self( array ))
704    }
705}
706
707impl<const SIZE: usize> Bytes<SIZE> {
708    /// Creates a new `Bytes` instance filled with zeros.
709    ///
710    /// This is a const function, allowing it to be used in const contexts
711    /// and static variable declarations.
712    ///
713    /// # Returns
714    ///
715    /// A `Bytes` instance with all bytes set to 0.
716    ///
717    /// # Examples
718    ///
719    /// ```ignore
720    /// use osal_rs::utils::Bytes;
721    /// 
722    /// const BUFFER: Bytes<64> = Bytes::new();
723    /// 
724    /// let runtime_buffer = Bytes::<32>::new();
725    /// assert_eq!(runtime_buffer[0], 0);
726    /// ```
727    pub const fn new() -> Self {
728        Self( [0u8; SIZE] )
729    }
730
731    /// Creates a new `Bytes` instance from a string slice.
732    ///
733    /// Copies the bytes from the input string into the fixed-size array.
734    /// If the string is shorter than `SIZE`, the remaining bytes are zero-filled.
735    /// If the string is longer, it is truncated to fit.
736    ///
737    /// # Parameters
738    ///
739    /// * `str` - The source string to convert
740    ///
741    /// # Returns
742    ///
743    /// A `Bytes` instance containing the string data.
744    ///
745    /// # Examples
746    ///
747    /// ```ignore
748    /// use osal_rs::utils::Bytes;
749    ///
750    /// let short = Bytes::<16>::new_by_str("Hi");
751    /// // Internal array: [b'H', b'i', 0, 0, 0, ...]
752    ///
753    /// let exact = Bytes::<5>::new_by_str("Hello");
754    /// // Internal array: [b'H', b'e', b'l', b'l', b'o']
755    ///
756    /// let long = Bytes::<3>::new_by_str("Hello");
757    /// // Internal array: [b'H', b'e', b'l'] (truncated)
758    /// ```
759    pub fn new_by_str(str: &str) -> Self {
760
761        let mut array = [0u8; SIZE];
762
763        let mut i = 0usize ;
764        for byte in str.as_bytes() {
765            if i > SIZE - 1{
766                break;
767            }
768            array[i] = *byte;
769            i += 1;
770        }
771
772        Self( array )
773    }
774
775    /// Creates a new `Bytes` instance from a C string pointer.
776    ///
777    /// Safely converts a null-terminated C string pointer into a `Bytes` instance.
778    /// If the pointer is null, returns a zero-initialized `Bytes`. The function
779    /// copies bytes from the C string into the fixed-size array, truncating if
780    /// the source is longer than `SIZE`.
781    ///
782    /// # Parameters
783    ///
784    /// * `str` - A pointer to a null-terminated C string (`*const c_char`)
785    ///
786    /// # Safety
787    ///
788    /// While this function is not marked unsafe, it internally uses `unsafe` code
789    /// to dereference the pointer. The caller must ensure that:
790    /// - If not null, the pointer points to a valid null-terminated C string
791    /// - The memory the pointer references remains valid for the duration of the call
792    ///
793    /// # Returns
794    ///
795    /// A `Bytes` instance containing the C string data, or zero-initialized if the pointer is null.
796    ///
797    /// # Examples
798    ///
799    /// ```ignore
800    /// use osal_rs::utils::Bytes;
801    /// use core::ffi::c_char;
802    /// use alloc::ffi::CString;
803    ///
804    /// // From a CString
805    /// let c_string = CString::new("Hello").unwrap();
806    /// let bytes = Bytes::<16>::new_by_ptr(c_string.as_ptr());
807    ///
808    /// // From a null pointer
809    /// let null_bytes = Bytes::<16>::new_by_ptr(core::ptr::null());
810    /// // Returns zero-initialized Bytes
811    ///
812    /// // Truncation example
813    /// let long_string = CString::new("This is a very long string").unwrap();
814    /// let short_bytes = Bytes::<8>::new_by_ptr(long_string.as_ptr());
815    /// // Only first 8 bytes are copied
816    /// ```
817    pub fn new_by_ptr(str: *const c_char) -> Self {
818        if str.is_null() {
819            return Self::new();
820        }
821
822        let mut array = [0u8; SIZE];
823
824        let mut i = 0usize ;
825        for byte in unsafe { CStr::from_ptr(str) }.to_bytes() {
826            if i > SIZE - 1{
827                break;
828            }
829            array[i] = *byte;
830            i += 1;
831        }
832
833        Self( array )
834    }
835
836    /// Creates a new `Bytes` instance from any type implementing `ToString`.
837    ///
838    /// This is a convenience wrapper around [`new_by_str`](Self::new_by_str)
839    /// that first converts the input to a string.
840    ///
841    /// # Parameters
842    ///
843    /// * `str` - Any value that implements `ToString`
844    ///
845    /// # Returns
846    ///
847    /// A `Bytes` instance containing the string representation of the input.
848    ///
849    /// # Examples
850    ///
851    /// ```ignore
852    /// use osal_rs::utils::Bytes;
853    ///
854    /// // From integer
855    /// let num_bytes = Bytes::<8>::new_by_string(&42);
856    ///
857    /// // From String
858    /// let string = String::from("Task");
859    /// let str_bytes = Bytes::<16>::new_by_string(&string);
860    ///
861    /// // From custom type with ToString
862    /// #[derive(Debug)]
863    /// struct TaskId(u32);
864    /// impl ToString for TaskId {
865    ///     fn to_string(&self) -> String {
866    ///         format!("Task-{}", self.0)
867    ///     }
868    /// }
869    /// let task_bytes = Bytes::<16>::new_by_string(&TaskId(5));
870    /// ```
871    pub fn new_by_as_sync_str(str: &impl ToString) -> Self {
872        Self::new_by_str(&str.to_string())
873    }
874
875    pub fn new_by_bytes(bytes: &[u8]) -> Self {
876        let mut array = [0u8; SIZE];
877        let len = core::cmp::min(bytes.len(), SIZE);
878        array[..len].copy_from_slice(&bytes[..len]);
879        Self( array )
880    }
881
882    /// Fills a mutable string slice with the contents of the byte array.
883    ///
884    /// Attempts to convert the internal byte array to a UTF-8 string and
885    /// copies it into the destination string slice. Only copies up to the
886    /// minimum of the source and destination lengths.
887    ///
888    /// # Parameters
889    ///
890    /// * `dest` - The destination string slice to fill
891    ///
892    /// # Returns
893    ///
894    /// `Ok(())` if the operation succeeds, or `Err(Error::StringConversionError)` if the byte array cannot be converted to a valid UTF-8 string.
895    ///
896    /// # Examples
897    ///
898    /// ```ignore
899    /// use osal_rs::utils::Bytes;
900    /// 
901    /// let bytes = Bytes::<16>::new_by_str("Hello World");
902    /// 
903    /// let mut output = String::from("                "); // 16 spaces
904    /// bytes.fill_str(unsafe { output.as_mut_str() });
905    /// 
906    /// assert_eq!(&output[..11], "Hello World");
907    /// ```
908    pub fn fill_str(&mut self, dest: &mut str) -> Result<()>{
909        match from_utf8_mut(&mut self.0) {
910            Ok(str) => {
911                let len = core::cmp::min(str.len(), dest.len());
912                unsafe {
913                    dest.as_bytes_mut()[..len].copy_from_slice(&str.as_bytes()[..len]);
914                }
915                Ok(())
916            }
917            Err(_) => Err(Error::StringConversionError),
918        }
919    }
920
921    /// Creates a new `Bytes` instance from a C string pointer.
922    ///
923    /// This is a convenience wrapper around [`new_by_ptr`](Self::new_by_ptr) that directly converts a C string pointer to a `Bytes` instance.
924    /// If the pointer is null, it returns a zero-initialized `Bytes`. The function copies bytes from the C string into the fixed-size array, truncating if the source is longer than `SIZE`.
925    ///
926    /// # Parameters
927    ///
928    /// * `str` - A pointer to a null-terminated C string (`*const c_char`)
929    ///
930    /// # Safety
931    ///
932    /// This method uses `unsafe` code to dereference the pointer. The caller must ensure that:
933    /// - If not null, the pointer points to a valid null-terminated C string
934    /// - The memory the pointer references remains valid for the duration of the call
935    ///
936    /// - The byte array can be safely interpreted as UTF-8 if the conversion is expected to succeed. If the byte array contains invalid UTF-8, the resulting `Bytes` instance will contain the raw bytes, and the `Display` implementation will show "Conversion error" when attempting to display it as a string.
937    ///
938    /// # Returns
939    ///
940    /// A `Bytes` instance containing the C string data, or zero-initialized if the pointer is null.
941    ///
942    /// # Examples
943    ///
944    /// ```ignore
945    /// use osal_rs::utils::Bytes;
946    /// use core::ffi::c_char;
947    /// use alloc::ffi::CString;
948    /// 
949    /// // From a CString
950    /// let c_string = CString::new("Hello").unwrap();
951    /// let bytes = Bytes::<16>::from_cstr(c_string.as_ptr());
952    /// 
953    /// // From a null pointer
954    /// let null_bytes = Bytes::<16>::from_cstr(core::ptr::null());
955    /// // Returns zero-initialized Bytes
956    /// 
957    /// // Truncation example
958    /// let long_string = CString::new("This is a very long string").unwrap();
959    /// let short_bytes = Bytes::<8>::from_cstr(long_string.as_ptr());
960    /// // Only first 8 bytes are copied
961    /// ```
962    pub fn from_cstr(str: *const c_char) -> Self {
963        Self::new_by_bytes(unsafe { CStr::from_ptr(str) }.to_bytes())
964    }
965
966    /// Converts the byte array to a C string reference.
967    ///
968    /// Creates a `CStr` reference from the internal byte array, treating it as
969    /// a null-terminated C string. This is useful for passing strings to C FFI
970    /// functions that expect `*const c_char` or `&CStr`.
971    ///
972    /// # Safety
973    ///
974    /// This method is unsafe because it assumes:
975    /// - The byte array contains valid UTF-8 data
976    /// - The byte array is null-terminated
977    /// - There are no interior null bytes before the terminating null
978    ///
979    /// Violating these assumptions may lead to undefined behavior.
980    ///
981    /// # Returns
982    ///
983    /// A reference to a `CStr` with lifetime tied to `self`.
984    ///
985    /// # Examples
986    ///
987    /// ```ignore
988    /// use osal_rs::utils::Bytes;
989    /// 
990    /// let bytes = Bytes::<16>::new_by_str("Hello");
991    /// let c_str = bytes.as_c_str();
992    /// 
993    /// extern "C" {
994    ///     fn print_string(s: *const core::ffi::c_char);
995    /// }
996    /// 
997    /// unsafe {
998    ///     print_string(c_str.as_ptr());
999    /// }
1000    /// ```
1001    pub fn as_cstr(&self) -> &CStr {
1002        unsafe {
1003            CStr::from_ptr(self.0.as_ptr() as *const c_char)
1004        }
1005    }
1006
1007    /// Converts the byte array to an owned C string.
1008    ///
1009    /// Creates a new `CString` by copying the contents of the internal byte array.
1010    /// Unlike [`as_cstr`](Self::as_cstr), this method allocates heap memory and
1011    /// returns an owned string that can outlive the original `Bytes` instance.
1012    ///
1013    /// # Safety
1014    ///
1015    /// This method uses `from_vec_unchecked` which assumes the byte array
1016    /// does not contain any interior null bytes. If this assumption is violated,
1017    /// the resulting `CString` will be invalid.
1018    ///
1019    /// # Returns
1020    ///
1021    /// An owned `CString` containing a copy of the byte array data.
1022    ///
1023    /// # Memory Allocation
1024    ///
1025    /// This method allocates on the heap. In memory-constrained embedded systems,
1026    /// prefer [`as_c_str`](Self::as_c_str) when possible.
1027    ///
1028    /// # Examples
1029    ///
1030    /// ```ignore
1031    /// use osal_rs::utils::Bytes;
1032    /// 
1033    /// fn process_name(bytes: &Bytes<16>) -> alloc::ffi::CString {
1034    ///     // Create an owned copy that can be returned
1035    ///     bytes.as_cstring()
1036    /// }
1037    /// 
1038    /// let name = Bytes::<16>::new_by_str("Task");
1039    /// let owned = process_name(&name);
1040    /// // 'name' can be dropped, 'owned' still valid
1041    /// ```
1042    pub fn as_cstring(&self) -> CString {
1043        unsafe {
1044            CString::from_vec_unchecked(self.0.to_vec())
1045        }
1046    }
1047
1048    /// Appends a string slice to the existing content in the `Bytes` buffer.
1049    ///
1050    /// This method finds the current end of the content (first null byte) and appends
1051    /// the provided string starting from that position. If the buffer is already full
1052    /// or if the appended content would exceed the buffer size, the content is truncated
1053    /// to fit within the `SIZE` limit.
1054    ///
1055    /// # Parameters
1056    ///
1057    /// * `str` - The string slice to append
1058    ///
1059    /// # Examples
1060    ///
1061    /// ```
1062    /// use osal_rs::utils::Bytes;
1063    ///
1064    /// let mut bytes = Bytes::<16>::new_by_str("Hello");
1065    /// bytes.append_str(" World");
1066    /// assert_eq!(bytes.as_str(), "Hello World");
1067    ///
1068    /// // Truncation when exceeding buffer size
1069    /// let mut small_bytes = Bytes::<8>::new_by_str("Hi");
1070    /// small_bytes.append_str(" there friend");
1071    /// assert_eq!(small_bytes.as_str(), "Hi ther");
1072    /// ```
1073    pub fn append_str(&mut self, str: &str) {
1074        let current_len = self.0.iter().position(|&b| b == 0).unwrap_or(SIZE);
1075        let mut i = current_len;
1076        for byte in str.as_bytes() {
1077            if i > SIZE - 1{
1078                break;
1079            }
1080            self.0[i] = *byte;
1081            i += 1;
1082        }
1083    }
1084
1085    /// Appends content from any type implementing `AsSyncStr` to the buffer.
1086    ///
1087    /// This method accepts any type that implements the `AsSyncStr` trait, converts
1088    /// it to a string slice, and appends it to the existing content. If the buffer
1089    /// is already full or if the appended content would exceed the buffer size,
1090    /// the content is truncated to fit within the `SIZE` limit.
1091    ///
1092    /// # Parameters
1093    ///
1094    /// * `c_str` - A reference to any type implementing `AsSyncStr`
1095    ///
1096    /// # Examples
1097    ///
1098    /// ```ignore
1099    /// use osal_rs::utils::Bytes;
1100    ///
1101    /// let mut bytes = Bytes::<16>::new_by_str("Hello");
1102    /// let other_bytes = Bytes::<8>::new_by_str(" World");
1103    /// bytes.append_as_sync_str(&other_bytes);
1104    /// assert_eq!(bytes.as_str(), "Hello World");
1105    /// ```
1106    pub fn append_as_sync_str(&mut self, c_str: & impl AsSyncStr) {
1107        let current_len = self.0.iter().position(|&b| b == 0).unwrap_or(SIZE);
1108        let mut i = current_len;
1109        for byte in c_str.as_str().as_bytes() {
1110            if i > SIZE - 1{
1111                break;
1112            }
1113            self.0[i] = *byte;
1114            i += 1;
1115        }
1116    }
1117
1118    /// Appends raw bytes to the existing content in the `Bytes` buffer.
1119    ///
1120    /// This method finds the current end of the content (first null byte) and appends
1121    /// the provided byte slice starting from that position. If the buffer is already
1122    /// full or if the appended content would exceed the buffer size, the content is
1123    /// truncated to fit within the `SIZE` limit.
1124    ///
1125    /// # Parameters
1126    ///
1127    /// * `bytes` - The byte slice to append
1128    ///
1129    /// # Examples
1130    ///
1131    /// ```
1132    /// use osal_rs::utils::Bytes;
1133    ///
1134    /// let mut bytes = Bytes::<16>::new_by_str("Hello");
1135    /// bytes.append_bytes(b" World");
1136    /// assert_eq!(bytes.as_str(), "Hello World");
1137    ///
1138    /// // Appending arbitrary bytes
1139    /// let mut data = Bytes::<16>::new_by_str("Data: ");
1140    /// data.append_bytes(&[0x41, 0x42, 0x43]);
1141    /// assert_eq!(data.as_str(), "Data: ABC");
1142    /// ```
1143    pub fn append_bytes(&mut self, bytes: &[u8]) {
1144        let current_len = self.0.iter().position(|&b| b == 0).unwrap_or(SIZE);
1145        let mut i = current_len;
1146        for byte in bytes {
1147            if i > SIZE - 1{
1148                break;
1149            }
1150            self.0[i] = *byte;
1151            i += 1;
1152        }
1153    }
1154
1155    /// Appends the content of another `Bytes` instance to this buffer.
1156    ///
1157    /// This method allows appending content from a `Bytes` instance of a different
1158    /// size (specified by the generic parameter `OHTER_SIZE`). The method finds the
1159    /// current end of the content (first null byte) and appends the content from the
1160    /// other `Bytes` instance. If the buffer is already full or if the appended content
1161    /// would exceed the buffer size, the content is truncated to fit within the `SIZE` limit.
1162    ///
1163    /// # Type Parameters
1164    ///
1165    /// * `OHTER_SIZE` - The size of the source `Bytes` buffer (can be different from `SIZE`)
1166    ///
1167    /// # Parameters
1168    ///
1169    /// * `other` - A reference to the `Bytes` instance to append
1170    ///
1171    /// # Examples
1172    ///
1173    /// ```
1174    /// use osal_rs::utils::Bytes;
1175    ///
1176    /// let mut bytes = Bytes::<16>::new_by_str("Hello");
1177    /// let other = Bytes::<8>::new_by_str(" World");
1178    /// bytes.append(&other);
1179    /// assert_eq!(bytes.as_str(), "Hello World");
1180    ///
1181    /// // Appending from a larger buffer
1182    /// let mut small = Bytes::<8>::new_by_str("Hi");
1183    /// let large = Bytes::<32>::new_by_str(" there friend");
1184    /// small.append(&large);
1185    /// assert_eq!(small.as_str(), "Hi ther");
1186    /// ```
1187    pub fn append<const OHTER_SIZE: usize>(&mut self, other: &Bytes<OHTER_SIZE>) {
1188        let current_len = self.0.iter().position(|&b| b == 0).unwrap_or(SIZE);
1189        let mut i = current_len;
1190        for &byte in other.0.iter() {
1191            if i > SIZE - 1{
1192                break;
1193            }
1194            self.0[i] = byte;
1195            i += 1;
1196        }
1197    }
1198
1199    /// Clears all content from the buffer, filling it with zeros.
1200    ///
1201    /// This method resets the entire internal byte array to zeros, effectively
1202    /// clearing any stored data. After calling this method, the buffer will be
1203    /// empty and ready for new content.
1204    ///
1205    /// # Examples
1206    ///
1207    /// ```ignore
1208    /// use osal_rs::utils::Bytes;
1209    ///
1210    /// let mut bytes = Bytes::<16>::new_by_str("Hello");
1211    /// assert!(!bytes.is_empty());
1212    ///
1213    /// bytes.clear();
1214    /// assert!(bytes.is_empty());
1215    /// assert_eq!(bytes.len(), 0);
1216    /// ```
1217    pub fn clear(&mut self) {
1218        for byte in self.0.iter_mut() {
1219            *byte = 0;
1220        }
1221    }
1222
1223    /// Returns the length of the content in the buffer.
1224    ///
1225    /// The length is determined by finding the position of the first null byte (0).
1226    /// If no null byte is found, returns `SIZE`, indicating the buffer is completely
1227    /// filled with non-zero data.
1228    ///
1229    /// # Returns
1230    ///
1231    /// The number of bytes before the first null terminator, or `SIZE` if the
1232    /// buffer is completely filled.
1233    ///
1234    /// # Examples
1235    ///
1236    /// ```ignore
1237    /// use osal_rs::utils::Bytes;
1238    ///
1239    /// let bytes = Bytes::<16>::new_by_str("Hello");
1240    /// assert_eq!(bytes.len(), 5);
1241    ///
1242    /// let empty = Bytes::<16>::new();
1243    /// assert_eq!(empty.len(), 0);
1244    ///
1245    /// // Buffer completely filled (no null terminator)
1246    /// let mut full = Bytes::<4>::new();
1247    /// full[0] = b'A';
1248    /// full[1] = b'B';
1249    /// full[2] = b'C';
1250    /// full[3] = b'D';
1251    /// assert_eq!(full.len(), 4);
1252    /// ```
1253    pub fn len(&self) -> usize {
1254        self.0.iter().position(|&b| b == 0).unwrap_or(SIZE)
1255    }
1256
1257    /// Checks if the buffer is empty.
1258    ///
1259    /// A buffer is considered empty if all bytes are zero. This method searches
1260    /// for the first non-zero byte to determine emptiness.
1261    ///
1262    /// # Returns
1263    ///
1264    /// `true` if all bytes are zero, `false` otherwise.
1265    ///
1266    /// # Examples
1267    ///
1268    /// ```ignore
1269    /// use osal_rs::utils::Bytes;
1270    ///
1271    /// let empty = Bytes::<16>::new();
1272    /// assert!(empty.is_empty());
1273    ///
1274    /// let bytes = Bytes::<16>::new_by_str("Hello");
1275    /// assert!(!bytes.is_empty());
1276    ///
1277    /// let mut cleared = Bytes::<16>::new_by_str("Test");
1278    /// cleared.clear();
1279    /// assert!(cleared.is_empty());
1280    /// ```
1281    pub fn is_empty(&self) -> bool {
1282        self.0.iter().position(|&b| b != 0).is_none()
1283    }
1284
1285    /// Returns the total capacity of the buffer.
1286    ///
1287    /// This is the fixed size of the internal byte array, determined at compile
1288    /// time by the generic `SIZE` parameter. The capacity never changes during
1289    /// the lifetime of the `Bytes` instance.
1290    ///
1291    /// # Returns
1292    ///
1293    /// The total capacity in bytes (`SIZE`).
1294    ///
1295    /// # Examples
1296    ///
1297    /// ```ignore
1298    /// use osal_rs::utils::Bytes;
1299    ///
1300    /// let bytes = Bytes::<32>::new();
1301    /// assert_eq!(bytes.capacity(), 32);
1302    ///
1303    /// let other = Bytes::<128>::new_by_str("Hello");
1304    /// assert_eq!(other.capacity(), 128);
1305    /// ```
1306    pub fn capacity(&self) -> usize {
1307        SIZE
1308    }
1309
1310    /// Replaces all occurrences of a byte pattern with another pattern.
1311    ///
1312    /// This method searches for all occurrences of the `find` byte sequence within
1313    /// the buffer and replaces them with the `replace` byte sequence. The replacement
1314    /// is performed in a single pass, and the method handles cases where the replacement
1315    /// is larger, smaller, or equal in size to the pattern being searched for.
1316    ///
1317    /// # Parameters
1318    ///
1319    /// * `find` - The byte pattern to search for
1320    /// * `replace` - The byte pattern to replace with
1321    ///
1322    /// # Returns
1323    ///
1324    /// * `Ok(())` - If all replacements were successful
1325    /// * `Err(Error::StringConversionError)` - If the replacement would exceed the buffer capacity
1326    ///
1327    /// # Behavior
1328    ///
1329    /// - Empty `find` patterns are ignored (returns `Ok(())` immediately)
1330    /// - Multiple occurrences are replaced in a single pass
1331    /// - Content is properly shifted when replacement size differs from find size
1332    /// - Null terminators and trailing bytes are correctly maintained
1333    /// - Overlapping patterns are not re-matched (avoids infinite loops)
1334    ///
1335    /// # Examples
1336    ///
1337    /// ```ignore
1338    /// use osal_rs::utils::Bytes;
1339    ///
1340    /// // Same length replacement
1341    /// let mut bytes = Bytes::<16>::new_by_str("Hello World");
1342    /// bytes.replace(b"World", b"Rust!").unwrap();
1343    /// assert_eq!(bytes.as_str(), "Hello Rust!");
1344    ///
1345    /// // Shorter replacement
1346    /// let mut bytes2 = Bytes::<16>::new_by_str("aabbcc");
1347    /// bytes2.replace(b"bb", b"X").unwrap();
1348    /// assert_eq!(bytes2.as_str(), "aaXcc");
1349    ///
1350    /// // Longer replacement
1351    /// let mut bytes3 = Bytes::<16>::new_by_str("Hi");
1352    /// bytes3.replace(b"Hi", b"Hello").unwrap();
1353    /// assert_eq!(bytes3.as_str(), "Hello");
1354    ///
1355    /// // Multiple occurrences
1356    /// let mut bytes4 = Bytes::<32>::new_by_str("foo bar foo");
1357    /// bytes4.replace(b"foo", b"baz").unwrap();
1358    /// assert_eq!(bytes4.as_str(), "baz bar baz");
1359    ///
1360    /// // Buffer overflow error
1361    /// let mut small = Bytes::<8>::new_by_str("Hello");
1362    /// assert!(small.replace(b"Hello", b"Hello World").is_err());
1363    /// ```
1364    pub fn replace(&mut self, find: &[u8], replace: &[u8]) -> Result<()> {
1365        if find.is_empty() {
1366            return Ok(());
1367        }
1368        
1369        let mut i = 0;
1370        loop {
1371            let current_len = self.len();
1372            
1373            // Exit if we've reached the end
1374            if i >= current_len {
1375                break;
1376            }
1377            
1378            // Check if pattern starts at position i
1379            if i + find.len() <= current_len && self.0[i..i + find.len()] == *find {
1380                let remaining_len = current_len - (i + find.len());
1381                let new_len = i + replace.len() + remaining_len;
1382                
1383                // Check if replacement fits in buffer
1384                if new_len > SIZE {
1385                    return Err(Error::StringConversionError);
1386                }
1387                
1388                // Shift remaining content if sizes differ
1389                if replace.len() != find.len() {
1390                    self.0.copy_within(
1391                        i + find.len()..i + find.len() + remaining_len,
1392                        i + replace.len()
1393                    );
1394                }
1395                
1396                // Insert replacement bytes
1397                self.0[i..i + replace.len()].copy_from_slice(replace);
1398                
1399                // Update null terminator position
1400                if new_len < SIZE {
1401                    self.0[new_len] = 0;
1402                }
1403                
1404                // Clear trailing bytes if content shrunk
1405                if new_len < current_len {
1406                    for j in (new_len + 1)..=current_len {
1407                        if j < SIZE {
1408                            self.0[j] = 0;
1409                        }
1410                    }
1411                }
1412                
1413                // Move past the replacement to avoid infinite loops
1414                i += replace.len();
1415            } else {
1416                i += 1;
1417            }
1418        }
1419        
1420        Ok(())
1421    }
1422
1423    /// Converts the `Bytes` instance to a byte slice.
1424    ///
1425    /// This method provides a convenient way to access the internal byte array
1426    /// as a slice, which can be useful for C FFI or other operations that
1427    /// require byte slices.
1428    ///
1429    /// # Examples
1430    ///
1431    /// ```ignore
1432    /// use osal_rs::utils::{Bytes, ToBytes};
1433    /// 
1434    /// let bytes = Bytes::<8>::new_by_str("example");
1435    /// let byte_slice = bytes.to_bytes();
1436    /// assert_eq!(byte_slice, b"example\0\0");
1437    /// ```
1438    pub fn to_bytes(&self) -> &[u8] {
1439        &self.0
1440    }
1441}
1442
1443/// Converts a byte slice to a hexadecimal string representation.
1444///
1445/// Each byte is converted to its two-character hexadecimal representation
1446/// in lowercase. This function allocates a new `String` on the heap.
1447///
1448/// # Parameters
1449///
1450/// * `bytes` - The byte slice to convert
1451///
1452/// # Returns
1453///
1454/// A `String` containing the hexadecimal representation of the bytes.
1455/// Each byte is represented by exactly 2 hex characters (lowercase).
1456///
1457/// # Memory Allocation
1458///
1459/// This function allocates heap memory. In memory-constrained environments,
1460/// consider using [`bytes_to_hex_into_slice`] instead.
1461///
1462/// # Examples
1463///
1464/// ```ignore
1465/// use osal_rs::utils::bytes_to_hex;
1466/// 
1467/// let data = &[0x01, 0x23, 0xAB, 0xFF];
1468/// let hex = bytes_to_hex(data);
1469/// assert_eq!(hex, "0123abff");
1470/// 
1471/// let empty = bytes_to_hex(&[]);
1472/// assert_eq!(empty, "");
1473/// ```
1474pub fn bytes_to_hex(bytes: &[u8]) -> String {
1475    bytes.iter()
1476         .map(|b| format!("{:02x}", b))
1477         .collect()
1478}
1479
1480/// Converts a byte slice to hexadecimal representation into a pre-allocated buffer.
1481///
1482/// This is a zero-allocation version of [`bytes_to_hex`] that writes the
1483/// hexadecimal representation directly into a provided output buffer.
1484/// Suitable for embedded systems and real-time applications.
1485///
1486/// # Parameters
1487///
1488/// * `bytes` - The source byte slice to convert
1489/// * `output` - The destination buffer to write hex characters into
1490///
1491/// # Returns
1492///
1493/// The number of bytes written to the output buffer (always `bytes.len() * 2`).
1494///
1495/// # Panics
1496///
1497/// Panics if `output.len() < bytes.len() * 2`. The output buffer must be
1498/// at least twice the size of the input to hold the hex representation.
1499///
1500/// # Examples
1501///
1502/// ```ignore
1503/// use osal_rs::utils::bytes_to_hex_into_slice;
1504/// 
1505/// let data = &[0x01, 0xAB, 0xFF];
1506/// let mut buffer = [0u8; 6];
1507/// 
1508/// let written = bytes_to_hex_into_slice(data, &mut buffer);
1509/// assert_eq!(written, 6);
1510/// assert_eq!(&buffer, b"01abff");
1511/// 
1512/// // Will panic - buffer too small
1513/// // let mut small = [0u8; 4];
1514/// // bytes_to_hex_into_slice(data, &mut small);
1515/// ```
1516pub fn bytes_to_hex_into_slice(bytes: &[u8], output: &mut [u8]) -> usize {
1517    assert!(output.len() >= bytes.len() * 2, "Buffer too small for hex conversion");
1518    let mut i = 0;
1519    for &b in bytes {
1520        let hex = format!("{:02x}", b);
1521        output[i..i+2].copy_from_slice(hex.as_bytes());
1522        i += 2;
1523    }
1524    i 
1525}
1526
1527/// Converts a hexadecimal string to a vector of bytes.
1528///
1529/// Parses a string of hexadecimal digits (case-insensitive) and converts
1530/// them to their binary representation. Each pair of hex digits becomes
1531/// one byte in the output.
1532///
1533/// # Parameters
1534///
1535/// * `hex` - A string slice containing hexadecimal digits (0-9, a-f, A-F)
1536///
1537/// # Returns
1538///
1539/// * `Ok(Vec<u8>)` - A vector containing the decoded bytes
1540/// * `Err(Error::StringConversionError)` - If the string has odd length or contains invalid hex digits
1541///
1542/// # Memory Allocation
1543///
1544/// This function allocates a `Vec` on the heap. For no-alloc environments,
1545/// use [`hex_to_bytes_into_slice`] instead.
1546///
1547/// # Examples
1548///
1549/// ```ignore
1550/// use osal_rs::utils::hex_to_bytes;
1551/// 
1552/// // Lowercase hex
1553/// let bytes = hex_to_bytes("0123abff").unwrap();
1554/// assert_eq!(bytes, vec![0x01, 0x23, 0xAB, 0xFF]);
1555/// 
1556/// // Uppercase hex
1557/// let bytes2 = hex_to_bytes("ABCD").unwrap();
1558/// assert_eq!(bytes2, vec![0xAB, 0xCD]);
1559/// 
1560/// // Odd length - error
1561/// assert!(hex_to_bytes("ABC").is_err());
1562/// 
1563/// // Invalid character - error
1564/// assert!(hex_to_bytes("0G").is_err());
1565/// ```
1566pub fn hex_to_bytes(hex: &str) -> Result<Vec<u8>> {
1567    if hex.len() % 2 != 0 {
1568        return Err(Error::StringConversionError);
1569    }
1570
1571    let bytes_result: Result<Vec<u8>> = (0..hex.len())
1572        .step_by(2)
1573        .map(|i| {
1574            u8::from_str_radix(&hex[i..i + 2], 16)
1575                .map_err(|_| Error::StringConversionError)
1576        })
1577        .collect();
1578
1579    bytes_result
1580}
1581
1582/// Converts a hexadecimal string to bytes into a pre-allocated buffer.
1583///
1584/// This is a zero-allocation version of [`hex_to_bytes`] that writes decoded
1585/// bytes directly into a provided output buffer. Suitable for embedded systems
1586/// and real-time applications where heap allocation is not desired.
1587///
1588/// # Parameters
1589///
1590/// * `hex` - A string slice containing hexadecimal digits (0-9, a-f, A-F)
1591/// * `output` - The destination buffer to write decoded bytes into
1592///
1593/// # Returns
1594///
1595/// * `Ok(usize)` - The number of bytes written to the output buffer (`hex.len() / 2`)
1596/// * `Err(Error::StringConversionError)` - If:
1597///   - The hex string has odd length
1598///   - The output buffer is too small (`output.len() < hex.len() / 2`)
1599///   - The hex string contains invalid characters
1600///
1601/// # Examples
1602///
1603/// ```ignore
1604/// use osal_rs::utils::hex_to_bytes_into_slice;
1605/// 
1606/// let mut buffer = [0u8; 4];
1607/// let written = hex_to_bytes_into_slice("0123abff", &mut buffer).unwrap();
1608/// assert_eq!(written, 4);
1609/// assert_eq!(buffer, [0x01, 0x23, 0xAB, 0xFF]);
1610/// 
1611/// // Buffer too small
1612/// let mut small = [0u8; 2];
1613/// assert!(hex_to_bytes_into_slice("0123abff", &mut small).is_err());
1614/// 
1615/// // Odd length string
1616/// assert!(hex_to_bytes_into_slice("ABC", &mut buffer).is_err());
1617/// ```
1618pub fn hex_to_bytes_into_slice(hex: &str, output: &mut [u8]) -> Result<usize> {
1619    if hex.len() % 2 != 0 || output.len() < hex.len() / 2 {
1620        return Err(Error::StringConversionError);
1621    }
1622
1623    for i in 0..(hex.len() / 2) {
1624        output[i] = u8::from_str_radix(&hex[2 * i..2 * i + 2], 16)
1625            .map_err(|_| Error::StringConversionError)?;
1626    }
1627
1628    Ok(hex.len() / 2)
1629}
1630
1631/// Thread-safe wrapper for `UnsafeCell` usable in `static` contexts.
1632///
1633/// `SyncUnsafeCell<T>` is a thin wrapper around `UnsafeCell<T>` that manually
1634/// implements the `Sync` and `Send` traits, allowing its use in `static` variables.
1635/// This is necessary in Rust 2024+ where `static mut` is no longer allowed.
1636///
1637/// # Safety
1638///
1639/// The manual implementation of `Sync` and `Send` is **unsafe** because the compiler
1640/// cannot verify that concurrent access is safe. It is the programmer's responsibility
1641/// to ensure that:
1642///
1643/// 1. In **single-threaded** environments (e.g., embedded bare-metal), there are no
1644///    synchronization issues since only one thread of execution exists.
1645///
1646/// 2. In **multi-threaded** environments, access to `SyncUnsafeCell` must be
1647///    externally protected via mutexes, critical sections, or other synchronization
1648///    primitives.
1649///
1650/// 3. No **data race** conditions occur during data access.
1651///
1652/// # Typical Usage
1653///
1654/// This structure is designed to replace `static mut` in embedded scenarios
1655/// where global mutability is necessary (e.g., hardware registers, shared buffers).
1656///
1657/// # Examples
1658///
1659/// ```ignore
1660/// use osal_rs::utils::SyncUnsafeCell;
1661///
1662/// // Global mutable variable in Rust 2024+
1663/// static COUNTER: SyncUnsafeCell<u32> = SyncUnsafeCell::new(0);
1664///
1665/// fn increment_counter() {
1666///     unsafe {
1667///         let counter = &mut *COUNTER.get();
1668///         *counter += 1;
1669///     }
1670/// }
1671/// ```
1672///
1673/// # Alternatives
1674///
1675/// For non-embedded code or when real synchronization is needed:
1676/// - Use `Mutex<T>` or `RwLock<T>` for thread-safe protection
1677/// - Use `AtomicUsize`, `AtomicBool`, etc. for simple atomic types
1678pub struct SyncUnsafeCell<T>(UnsafeCell<T>);
1679
1680/// Manual implementation of `Sync` for `SyncUnsafeCell<T>`.
1681///
1682/// # Safety
1683///
1684/// This is **unsafe** because it asserts that `SyncUnsafeCell<T>` can be shared
1685/// between threads without causing data races. The caller must ensure synchronization.
1686unsafe impl<T> Sync for SyncUnsafeCell<T> {}
1687
1688/// Manual implementation of `Send` for `SyncUnsafeCell<T>`.
1689///
1690/// # Safety
1691///
1692/// This is **unsafe** because it asserts that `SyncUnsafeCell<T>` can be transferred
1693/// between threads. The inner type `T` may not be `Send`, so the caller must handle
1694/// memory safety.
1695unsafe impl<T> Send for SyncUnsafeCell<T> {}
1696
1697impl<T> SyncUnsafeCell<T> {
1698    /// Creates a new instance of `SyncUnsafeCell<T>`.
1699    ///
1700    /// This is a `const` function, allowing initialization in static and
1701    /// constant contexts.
1702    ///
1703    /// # Parameters
1704    ///
1705    /// * `value` - The initial value to wrap
1706    ///
1707    /// # Examples
1708    ///
1709    /// ```ignore
1710    /// use osal_rs::utils::SyncUnsafeCell;
1711    ///
1712    /// static CONFIG: SyncUnsafeCell<u32> = SyncUnsafeCell::new(42);
1713    /// ```
1714    pub const fn new(value: T) -> Self {
1715        Self(UnsafeCell::new(value))
1716    }
1717    
1718    /// Gets a raw mutable pointer to the contained value.
1719    ///
1720    /// # Safety
1721    ///
1722    /// This function is **unsafe** because:
1723    /// - It returns a raw pointer that bypasses the borrow checker
1724    /// - The caller must ensure there are no mutable aliases
1725    /// - Dereferencing the pointer without synchronization can cause data races
1726    ///
1727    /// # Returns
1728    ///
1729    /// A raw mutable pointer `*mut T` to the inner value.
1730    ///
1731    /// # Examples
1732    ///
1733    /// ```ignore
1734    /// use osal_rs::utils::SyncUnsafeCell;
1735    ///
1736    /// static VALUE: SyncUnsafeCell<i32> = SyncUnsafeCell::new(0);
1737    ///
1738    /// unsafe {
1739    ///     let ptr = VALUE.get();
1740    ///     *ptr = 42;
1741    /// }
1742    /// ```
1743    pub unsafe fn get(&self) -> *mut T {
1744        self.0.get()
1745    }
1746}