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