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::ffi::{CStr, c_char, c_void};
26use core::str::from_utf8_mut;
27use core::fmt::{Debug, Display}; 
28use core::ops::{Deref, DerefMut};
29use core::time::Duration;
30
31use alloc::ffi::CString;
32use alloc::format;
33use alloc::string::{String, ToString};
34use alloc::sync::Arc;
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, Mutex};
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,
96    /// Read error occurred
97    ReadError,
98    /// Return error with code
99    ReturnWithCode(i32),
100    /// Unhandled error with description
101    Unhandled(&'static str)
102}
103
104/// CPU register size enumeration.
105///
106/// Identifies whether the target CPU uses 32-bit or 64-bit registers.
107/// This is used for platform-specific tick count overflow handling.
108#[derive(PartialEq, Eq, Clone, Copy, Debug)]
109pub enum CpuRegisterSize {
110    /// 64-bit CPU registers
111    Bit64,
112    /// 32-bit CPU registers
113    Bit32
114}
115
116/// Boolean type compatible with RTOS return values.
117///
118/// Many RTOS functions return 0 for success and non-zero for failure.
119/// This type provides a Rust-idiomatic way to work with such values.
120///
121/// # Examples
122///
123/// ```ignore
124/// use osal_rs::os::{Semaphore, SemaphoreFn};
125/// use osal_rs::utils::OsalRsBool;
126/// use core::time::Duration;
127/// 
128/// let sem = Semaphore::new(1, 1).unwrap();
129/// 
130/// match sem.wait(Duration::from_millis(100)) {
131///     OsalRsBool::True => println!("Acquired semaphore"),
132///     OsalRsBool::False => println!("Failed to acquire"),
133/// }
134/// 
135/// // Can also convert to bool
136/// if sem.signal().into() {
137///     println!("Semaphore signaled");
138/// }
139/// ```
140#[derive(PartialEq, Eq, Clone, Copy, Debug)]
141#[repr(u8)]
142pub enum OsalRsBool {
143    /// Operation failed or condition is false
144    False = 1,
145    /// Operation succeeded or condition is true
146    True = 0
147}
148
149/// Maximum delay constant for blocking operations.
150///
151/// When used as a timeout parameter, indicates the operation should
152/// block indefinitely until it succeeds.
153///
154/// # Examples
155///
156/// ```ignore
157/// use osal_rs::os::{Mutex, MutexFn};
158/// use osal_rs::utils::MAX_DELAY;
159/// 
160/// let mutex = Mutex::new(0);
161/// let guard = mutex.lock();  // Blocks forever if needed
162/// ```
163pub const MAX_DELAY: Duration = Duration::from_millis(usize::MAX as u64);
164
165/// Standard Result type for OSAL-RS operations.
166///
167/// Uses [`Error`] as the default error type.
168pub type Result<T, E = Error> = core::result::Result<T, E>;
169
170/// Pointer to pointer type for C FFI.
171pub type DoublePtr = *mut *mut c_void;
172
173/// Mutable pointer type for C FFI.
174pub type Ptr = *mut c_void;
175
176/// Const pointer type for C FFI.
177pub type ConstPtr = *const c_void;
178
179/// Shortcut for Arc<Mutex<T>>
180pub type ArcMux<T> = Arc<Mutex<T>>;
181
182/// Determines the CPU register size at compile time.
183///
184/// This constant function checks the size of `usize` to determine whether
185/// the target architecture uses 32-bit or 64-bit registers. This information
186/// is used for platform-specific optimizations and overflow handling.
187///
188/// # Returns
189///
190/// * [`CpuRegisterSize::Bit64`] - For 64-bit architectures
191/// * [`CpuRegisterSize::Bit32`] - For 32-bit architectures
192///
193/// # Examples
194///
195/// ```ignore
196/// use osal_rs::utils::{register_bit_size, CpuRegisterSize};
197/// 
198/// match register_bit_size() {
199///     CpuRegisterSize::Bit64 => println!("Running on 64-bit platform"),
200///     CpuRegisterSize::Bit32 => println!("Running on 32-bit platform"),
201/// }
202/// ```
203pub const fn register_bit_size() -> CpuRegisterSize {
204    if size_of::<usize>() == 8 {
205        CpuRegisterSize::Bit64
206    } else {
207        CpuRegisterSize::Bit32
208    }
209}
210
211/// Converts a C string pointer to a Rust String.
212///
213/// This macro safely converts a raw C string pointer (`*const c_char`) into
214/// a Rust `String`. It handles UTF-8 conversion gracefully using lossy conversion.
215///
216/// # Safety
217///
218/// The pointer must be valid and point to a null-terminated C string.
219///
220/// # Examples
221///
222/// ```ignore
223/// use osal_rs::from_c_str;
224/// use core::ffi::c_char;
225/// 
226/// extern "C" {
227///     fn get_system_name() -> *const c_char;
228/// }
229/// 
230/// let name = from_c_str!(get_system_name());
231/// println!("System: {}", name);
232/// ```
233#[macro_export]
234macro_rules! from_c_str {
235    ($str:expr) => {
236        unsafe {
237            let c_str = core::ffi::CStr::from_ptr($str);
238            alloc::string::String::from_utf8_lossy(c_str.to_bytes()).to_string()
239        }
240    };
241}
242
243/// Converts a Rust string to a CString with error handling.
244///
245/// This macro creates a `CString` from a Rust string reference, returning
246/// a `Result` that can be used with the `?` operator. If the conversion fails
247/// (e.g., due to interior null bytes), it returns an appropriate error.
248///
249/// # Returns
250///
251/// * `Ok(CString)` - On successful conversion
252/// * `Err(Error::Unhandled)` - If the string contains interior null bytes
253///
254/// # Examples
255///
256/// ```ignore
257/// use osal_rs::to_cstring;
258/// use osal_rs::utils::Result;
259/// 
260/// fn pass_to_c_api(name: &str) -> Result<()> {
261///     let c_name = to_cstring!(name)?;
262///     // Use c_name.as_ptr() with C FFI
263///     Ok(())
264/// }
265/// ```
266#[macro_export]
267macro_rules! to_cstring {
268    ($s:expr) => {
269        alloc::ffi::CString::new($s.as_str())
270            .map_err(|_| $crate::utils::Error::Unhandled("Failed to convert string to CString"))
271    };
272}
273
274/// Converts a Rust string to a C string pointer.
275///
276/// This macro creates a `CString` from a Rust string and returns its raw pointer.
277/// **Warning**: This macro panics if the conversion fails. Consider using
278/// [`to_cstring!`] for safer error handling.
279///
280/// # Panics
281///
282/// Panics if the string contains interior null bytes.
283///
284/// # Examples
285///
286/// ```ignore
287/// use osal_rs::to_c_str;
288/// 
289/// extern "C" {
290///     fn set_name(name: *const core::ffi::c_char);
291/// }
292/// 
293/// let name = "FreeRTOS Task";
294/// unsafe {
295///     set_name(to_c_str!(name));
296/// }
297/// ```
298#[macro_export]
299macro_rules! to_c_str {
300    ($s:expr) => {
301        alloc::ffi::CString::new($s.as_ref() as &str).unwrap().as_ptr()
302    };
303}
304
305/// Converts a string to a fixed-size byte array.
306///
307/// This macro creates a byte array of the specified size and fills it with
308/// the bytes from the input string. If the string is shorter than the buffer,
309/// the remaining bytes are filled with spaces. If the string is longer, it
310/// is truncated to fit.
311///
312/// # Parameters
313///
314/// * `$str` - The source string to convert
315/// * `$buff_name` - The identifier name for the created buffer variable
316/// * `$buff_size` - The size of the byte array to create
317///
318/// # Examples
319///
320/// ```ignore
321/// use osal_rs::from_str_to_array;
322/// 
323/// let task_name = "MainTask";
324/// from_str_to_array!(task_name, name_buffer, 16);
325/// // name_buffer is now [u8; 16] containing "MainTask        "
326/// 
327/// // Use with C FFI
328/// extern "C" {
329///     fn create_task(name: *const u8, len: usize);
330/// }
331/// 
332/// unsafe {
333///     create_task(name_buffer.as_ptr(), name_buffer.len());
334/// }
335/// ```
336#[macro_export]
337macro_rules! from_str_to_array {
338    ($str:expr, $buff_name:ident, $buff_size:expr) => {
339        let mut $buff_name = [b' '; $buff_size];
340        let _bytes = $str.as_bytes();
341        let _len = core::cmp::min(_bytes.len(), $buff_size);
342        $buff_name[.._len].copy_from_slice(&_bytes[.._len]);
343    };
344}
345
346/// Extracts a typed parameter from an optional boxed Any reference.
347///
348/// This macro is used in thread/task entry points to safely extract and
349/// downcast parameters passed to the thread. It handles both the Option
350/// unwrapping and the type downcast, returning appropriate errors if either
351/// operation fails.
352///
353/// # Parameters
354///
355/// * `$param` - An `Option<Box<dyn Any>>` containing the parameter
356/// * `$t` - The type to downcast the parameter to
357///
358/// # Returns
359///
360/// * A reference to the downcasted value of type `$t`
361/// * `Err(Error::NullPtr)` - If the parameter is None
362/// * `Err(Error::InvalidType)` - If the downcast fails
363///
364/// # Examples
365///
366/// ```ignore
367/// use osal_rs::thread_extract_param;
368/// use osal_rs::utils::Result;
369/// use core::any::Any;
370/// 
371/// struct TaskConfig {
372///     priority: u8,
373///     stack_size: usize,
374/// }
375/// 
376/// fn task_entry(param: Option<Box<dyn Any>>) -> Result<()> {
377///     let config = thread_extract_param!(param, TaskConfig);
378///     
379///     println!("Priority: {}", config.priority);
380///     println!("Stack: {}", config.stack_size);
381///     
382///     Ok(())
383/// }
384/// ```
385#[macro_export]
386macro_rules! thread_extract_param {
387    ($param:expr, $t:ty) => {
388        match $param.as_ref() {
389            Some(p) => {
390                match p.downcast_ref::<$t>() {
391                    Some(value) => value,
392                    None => return Err($crate::utils::Error::InvalidType),
393                }
394            }
395            None => return Err($crate::utils::Error::NullPtr),
396        }
397    };
398}
399
400/// Creates an Arc<Mutex<T>> from a value.
401///
402/// This is a convenience macro to reduce boilerplate when creating
403/// thread-safe shared data structures.
404///
405/// # Examples
406///
407/// ```ignore
408/// use osal_rs::arcmux;
409/// 
410/// let shared_counter = arcmux!(0);
411/// // Equivalent to: Arc::new(Mutex::new(0))
412/// ```
413#[macro_export]
414macro_rules! arcmux {
415    ($value:expr) => {
416        alloc::sync::Arc::new($crate::os::Mutex::new($value))
417    };
418}
419
420
421
422/// Fixed-size byte array wrapper with string conversion utilities.
423///
424/// `Bytes` is a generic wrapper around a fixed-size byte array that provides
425/// convenient methods for converting between strings and byte arrays. It's
426/// particularly useful for interfacing with C APIs that expect fixed-size
427/// character buffers, or for storing strings in embedded systems with
428/// constrained memory.
429///
430/// # Type Parameters
431///
432/// * `SIZE` - The size of the internal byte array (default: 0)
433///
434/// # Examples
435///
436/// ```ignore
437/// use osal_rs::utils::Bytes;
438/// 
439/// // Create an empty 32-byte buffer
440/// let mut buffer = Bytes::<32>::new();
441/// 
442/// // Create a buffer from a string
443/// let name = Bytes::<16>::new_by_str("TaskName");
444/// println!("{}", name); // Prints "TaskName"
445/// 
446/// // Create from any type that implements ToString
447/// let number = 42;
448/// let num_bytes = Bytes::<8>::new_by_string(&number);
449/// ```
450
451#[cfg(feature = "serde")]
452#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
453pub struct Bytes<const SIZE: usize> (pub [u8; SIZE]);
454
455#[cfg(not(feature = "serde"))]
456#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
457pub struct Bytes<const SIZE: usize> (pub [u8; SIZE]);
458
459impl<const SIZE: usize> Deref for Bytes<SIZE> {
460    type Target = [u8; SIZE];
461
462    /// Dereferences to the underlying byte array.
463    ///
464    /// This allows `Bytes` to be used anywhere a `[u8; SIZE]` reference is expected.
465    ///
466    /// # Examples
467    ///
468    /// ```ignore
469    /// use osal_rs::utils::Bytes;
470    /// 
471    /// let bytes = Bytes::<8>::new_by_str("test");
472    /// assert_eq!(bytes[0], b't');
473    /// ```
474    fn deref(&self) -> &Self::Target {
475        &self.0
476    }
477}
478
479impl<const SIZE: usize> DerefMut for Bytes<SIZE> {
480    /// Provides mutable access to the underlying byte array.
481    ///
482    /// This allows `Bytes` to be mutably dereferenced, enabling direct modification
483    /// of the internal byte array through the `DerefMut` trait.
484    ///
485    /// # Examples
486    ///
487    /// ```ignore
488    /// use osal_rs::utils::Bytes;
489    /// 
490    /// let mut bytes = Bytes::<8>::new();
491    /// bytes[0] = b'H';
492    /// bytes[1] = b'i';
493    /// assert_eq!(bytes[0], b'H');
494    /// ```
495    fn deref_mut(&mut self) -> &mut Self::Target {
496        &mut self.0
497    }
498}
499
500impl<const SIZE: usize> Display for Bytes<SIZE> {
501    /// Formats the byte array as a C-style null-terminated string.
502    ///
503    /// This implementation treats the byte array as a C string and converts it
504    /// to a Rust string for display. If the conversion fails, it displays
505    /// "Conversion error".
506    ///
507    /// # Safety
508    ///
509    /// This method assumes the byte array contains valid UTF-8 data and is
510    /// null-terminated. Invalid data may result in the error message being displayed.
511    ///
512    /// # Examples
513    ///
514    /// ```ignore
515    /// use osal_rs::utils::Bytes;
516    /// 
517    /// let bytes = Bytes::<16>::new_by_str("Hello");
518    /// println!("{}", bytes); // Prints "Hello"
519    /// ```
520    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
521        let str = unsafe {
522            CStr::from_ptr(self.0.as_ptr() as *const c_char)
523            .to_str()
524            .unwrap_or("Conversion error")
525        };
526        
527        write!(f, "{}", str.to_string())
528    }
529}
530
531impl<const SIZE: usize> AsSyncStr for Bytes<SIZE> {
532    /// Returns a string slice reference.
533    ///
534    /// This method provides access to the underlying string data in a way
535    /// that is safe to use across thread boundaries.
536    ///
537    /// # Returns
538    ///
539    /// A reference to a string slice with lifetime tied to `self`.
540    fn as_str(&self) -> &str {
541        unsafe {
542            CStr::from_ptr(self.0.as_ptr() as *const c_char)
543            .to_str()
544            .unwrap_or("Conversion error")
545        }
546    }
547}
548
549/// Serialization implementation for `Bytes<SIZE>` when the `serde` feature is enabled.
550///
551/// This implementation provides serialization by directly serializing each byte
552/// in the array using the osal-rs-serde serialization framework.
553#[cfg(feature = "serde")]
554impl<const SIZE: usize> Serialize for Bytes<SIZE> {
555    /// Serializes the `Bytes` instance using the given serializer.
556    ///
557    /// # Parameters
558    ///
559    /// * `serializer` - The serializer to use
560    ///
561    /// # Returns
562    ///
563    /// * `Ok(())` - On successful serialization
564    /// * `Err(S::Error)` - If serialization fails
565    fn serialize<S: osal_rs_serde::Serializer>(&self, serializer: &mut S) -> core::result::Result<(), S::Error> {
566        for &byte in self.0.iter() {
567            serializer.serialize_u8("", byte)?;
568        }
569        Ok(())
570    }
571}
572
573/// Deserialization implementation for `Bytes<SIZE>` when the `serde` feature is enabled.
574///
575/// This implementation provides deserialization by reading bytes from the deserializer
576/// into a fixed-size array using the osal-rs-serde deserialization framework.
577#[cfg(feature = "serde")]
578impl<const SIZE: usize> Deserialize for Bytes<SIZE> {
579    /// Deserializes a `Bytes` instance using the given deserializer.
580    ///
581    /// # Parameters
582    ///
583    /// * `deserializer` - The deserializer to use
584    ///
585    /// # Returns
586    ///
587    /// * `Ok(Bytes<SIZE>)` - A new `Bytes` instance with deserialized data
588    /// * `Err(D::Error)` - If deserialization fails
589    fn deserialize<D: osal_rs_serde::Deserializer>(deserializer: &mut D, name: &str) -> core::result::Result<Self, D::Error> {
590        let mut array = [0u8; SIZE];
591        for i in 0..SIZE {
592            array[i] = deserializer.deserialize_u8(name)?;
593        }
594        Ok(Self(array))
595    }
596}
597
598/// Serialization implementation for `Bytes<SIZE>` when the `serde` feature is disabled.
599///
600/// This implementation provides basic serialization by directly returning a reference
601/// to the underlying byte array. It's used when the library is compiled without the
602/// `serde` feature, providing a lightweight alternative serialization mechanism.
603#[cfg(not(feature = "serde"))]
604impl<const SIZE: usize> Serialize for Bytes<SIZE> {
605    /// Converts the `Bytes` instance to a byte slice.
606    ///
607    /// # Returns
608    ///
609    /// A reference to the internal byte array.
610    fn to_bytes(&self) -> &[u8] {
611        &self.0
612    }
613}
614
615/// Deserialization implementation for `Bytes<SIZE>` when the `serde` feature is disabled.
616///
617/// This implementation provides basic deserialization by copying bytes from a slice
618/// into a fixed-size array. If the source slice is shorter than `SIZE`, the remaining
619/// bytes are zero-filled. If longer, it's truncated to fit.
620#[cfg(not(feature = "serde"))]
621impl<const SIZE: usize> Deserialize for Bytes<SIZE> {
622    /// Creates a `Bytes` instance from a byte slice.
623    ///
624    /// # Parameters
625    ///
626    /// * `bytes` - The source byte slice to deserialize from
627    ///
628    /// # Returns
629    ///
630    /// * `Ok(Bytes<SIZE>)` - A new `Bytes` instance with data copied from the slice
631    ///
632    /// # Examples
633    ///
634    /// ```ignore
635    /// use osal_rs::utils::Bytes;
636    /// use osal_rs::os::Deserialize;
637    /// 
638    /// let data = b"Hello";
639    /// let bytes = Bytes::<16>::from_bytes(data).unwrap();
640    /// // Result: [b'H', b'e', b'l', b'l', b'o', 0, 0, 0, ...]
641    /// ```
642    fn from_bytes(bytes: &[u8]) -> Result<Self> {
643        let mut array = [0u8; SIZE];
644        let len = core::cmp::min(bytes.len(), SIZE);
645        array[..len].copy_from_slice(&bytes[..len]);
646        Ok(Self( array ))
647    }
648}
649
650impl<const SIZE: usize> Bytes<SIZE> {
651    /// Creates a new `Bytes` instance filled with zeros.
652    ///
653    /// This is a const function, allowing it to be used in const contexts
654    /// and static variable declarations.
655    ///
656    /// # Returns
657    ///
658    /// A `Bytes` instance with all bytes set to 0.
659    ///
660    /// # Examples
661    ///
662    /// ```ignore
663    /// use osal_rs::utils::Bytes;
664    /// 
665    /// const BUFFER: Bytes<64> = Bytes::new();
666    /// 
667    /// let runtime_buffer = Bytes::<32>::new();
668    /// assert_eq!(runtime_buffer[0], 0);
669    /// ```
670    pub const fn new() -> Self {
671        Self( [0u8; SIZE] )
672    }
673
674    /// Creates a new `Bytes` instance from a string slice.
675    ///
676    /// Copies the bytes from the input string into the fixed-size array.
677    /// If the string is shorter than `SIZE`, the remaining bytes are zero-filled.
678    /// If the string is longer, it is truncated to fit.
679    ///
680    /// # Parameters
681    ///
682    /// * `str` - The source string to convert
683    ///
684    /// # Returns
685    ///
686    /// A `Bytes` instance containing the string data.
687    ///
688    /// # Examples
689    ///
690    /// ```ignore
691    /// use osal_rs::utils::Bytes;
692    /// 
693    /// let short = Bytes::<16>::new_by_str("Hi");
694    /// // Internal array: [b'H', b'i', 0, 0, 0, ...]
695    /// 
696    /// let exact = Bytes::<5>::new_by_str("Hello");
697    /// // Internal array: [b'H', b'e', b'l', b'l', b'o']
698    /// 
699    /// let long = Bytes::<3>::new_by_str("Hello");
700    /// // Internal array: [b'H', b'e', b'l'] (truncated)
701    /// ```
702    pub fn new_by_str(str: &str) -> Self {
703
704        let mut array = [0u8; SIZE];
705        
706        let mut i = 0usize ;
707        for byte in str.as_bytes() {
708            if i > SIZE - 1{
709                break;
710            }
711            array[i] = *byte;
712            i += 1;
713        }  
714
715        Self( array )
716    }
717
718    /// Creates a new `Bytes` instance from any type implementing `ToString`.
719    ///
720    /// This is a convenience wrapper around [`new_by_str`](Self::new_by_str)
721    /// that first converts the input to a string.
722    ///
723    /// # Parameters
724    ///
725    /// * `str` - Any value that implements `ToString`
726    ///
727    /// # Returns
728    ///
729    /// A `Bytes` instance containing the string representation of the input.
730    ///
731    /// # Examples
732    ///
733    /// ```ignore
734    /// use osal_rs::utils::Bytes;
735    /// 
736    /// // From integer
737    /// let num_bytes = Bytes::<8>::new_by_string(&42);
738    /// 
739    /// // From String
740    /// let string = String::from("Task");
741    /// let str_bytes = Bytes::<16>::new_by_string(&string);
742    /// 
743    /// // From custom type with ToString
744    /// #[derive(Debug)]
745    /// struct TaskId(u32);
746    /// impl ToString for TaskId {
747    ///     fn to_string(&self) -> String {
748    ///         format!("Task-{}", self.0)
749    ///     }
750    /// }
751    /// let task_bytes = Bytes::<16>::new_by_string(&TaskId(5));
752    /// ```
753    pub fn new_by_string(str: &impl ToString) -> Self {
754        Self::new_by_str(&str.to_string())
755    }
756
757    /// Fills a mutable string slice with the contents of the byte array.
758    ///
759    /// Attempts to convert the internal byte array to a UTF-8 string and
760    /// copies it into the destination string slice. Only copies up to the
761    /// minimum of the source and destination lengths.
762    ///
763    /// # Parameters
764    ///
765    /// * `dest` - The destination string slice to fill
766    ///
767    /// # Panics
768    ///
769    /// Currently panics (todo!) if the byte array contains invalid UTF-8.
770    ///
771    /// # Examples
772    ///
773    /// ```ignore
774    /// use osal_rs::utils::Bytes;
775    /// 
776    /// let bytes = Bytes::<16>::new_by_str("Hello World");
777    /// 
778    /// let mut output = String::from("                "); // 16 spaces
779    /// bytes.fill_str(unsafe { output.as_mut_str() });
780    /// 
781    /// assert_eq!(&output[..11], "Hello World");
782    /// ```
783    pub fn fill_str(&mut self, dest: &mut str) {
784        match from_utf8_mut(&mut self.0) {
785            Ok(str) => {
786                let len = core::cmp::min(str.len(), dest.len());
787                unsafe {
788                    dest.as_bytes_mut()[..len].copy_from_slice(&str.as_bytes()[..len]);
789                }
790            }
791            Err(_) => todo!(),
792        }
793    }
794
795    /// Converts the byte array to a C string reference.
796    ///
797    /// Creates a `CStr` reference from the internal byte array, treating it as
798    /// a null-terminated C string. This is useful for passing strings to C FFI
799    /// functions that expect `*const c_char` or `&CStr`.
800    ///
801    /// # Safety
802    ///
803    /// This method is unsafe because it assumes:
804    /// - The byte array contains valid UTF-8 data
805    /// - The byte array is null-terminated
806    /// - There are no interior null bytes before the terminating null
807    ///
808    /// Violating these assumptions may lead to undefined behavior.
809    ///
810    /// # Returns
811    ///
812    /// A reference to a `CStr` with lifetime tied to `self`.
813    ///
814    /// # Examples
815    ///
816    /// ```ignore
817    /// use osal_rs::utils::Bytes;
818    /// 
819    /// let bytes = Bytes::<16>::new_by_str("Hello");
820    /// let c_str = bytes.as_c_str();
821    /// 
822    /// extern "C" {
823    ///     fn print_string(s: *const core::ffi::c_char);
824    /// }
825    /// 
826    /// unsafe {
827    ///     print_string(c_str.as_ptr());
828    /// }
829    /// ```
830    pub fn as_c_str(&self) -> &CStr {
831        unsafe {
832            CStr::from_ptr(self.0.as_ptr() as *const c_char)
833        }
834    }
835
836    /// Converts the byte array to an owned C string.
837    ///
838    /// Creates a new `CString` by copying the contents of the internal byte array.
839    /// Unlike [`as_c_str`](Self::as_c_str), this method allocates heap memory and
840    /// returns an owned string that can outlive the original `Bytes` instance.
841    ///
842    /// # Safety
843    ///
844    /// This method uses `from_vec_unchecked` which assumes the byte array
845    /// does not contain any interior null bytes. If this assumption is violated,
846    /// the resulting `CString` will be invalid.
847    ///
848    /// # Returns
849    ///
850    /// An owned `CString` containing a copy of the byte array data.
851    ///
852    /// # Memory Allocation
853    ///
854    /// This method allocates on the heap. In memory-constrained embedded systems,
855    /// prefer [`as_c_str`](Self::as_c_str) when possible.
856    ///
857    /// # Examples
858    ///
859    /// ```ignore
860    /// use osal_rs::utils::Bytes;
861    /// 
862    /// fn process_name(bytes: &Bytes<16>) -> alloc::ffi::CString {
863    ///     // Create an owned copy that can be returned
864    ///     bytes.as_cstring()
865    /// }
866    /// 
867    /// let name = Bytes::<16>::new_by_str("Task");
868    /// let owned = process_name(&name);
869    /// // 'name' can be dropped, 'owned' still valid
870    /// ```
871    pub fn as_cstring(&self) -> CString {
872        unsafe {
873            CString::from_vec_unchecked(self.0.to_vec())
874        }
875    }
876}
877
878/// Converts a byte slice to a hexadecimal string representation.
879///
880/// Each byte is converted to its two-character hexadecimal representation
881/// in lowercase. This function allocates a new `String` on the heap.
882///
883/// # Parameters
884///
885/// * `bytes` - The byte slice to convert
886///
887/// # Returns
888///
889/// A `String` containing the hexadecimal representation of the bytes.
890/// Each byte is represented by exactly 2 hex characters (lowercase).
891///
892/// # Memory Allocation
893///
894/// This function allocates heap memory. In memory-constrained environments,
895/// consider using [`bytes_to_hex_into_slice`] instead.
896///
897/// # Examples
898///
899/// ```ignore
900/// use osal_rs::utils::bytes_to_hex;
901/// 
902/// let data = &[0x01, 0x23, 0xAB, 0xFF];
903/// let hex = bytes_to_hex(data);
904/// assert_eq!(hex, "0123abff");
905/// 
906/// let empty = bytes_to_hex(&[]);
907/// assert_eq!(empty, "");
908/// ```
909pub fn bytes_to_hex<'a>(bytes: &'a [u8]) -> String {
910    bytes.iter()
911         .map(|b| format!("{:02x}", b))
912         .collect()
913}
914
915/// Converts a byte slice to hexadecimal representation into a pre-allocated buffer.
916///
917/// This is a zero-allocation version of [`bytes_to_hex`] that writes the
918/// hexadecimal representation directly into a provided output buffer.
919/// Suitable for embedded systems and real-time applications.
920///
921/// # Parameters
922///
923/// * `bytes` - The source byte slice to convert
924/// * `output` - The destination buffer to write hex characters into
925///
926/// # Returns
927///
928/// The number of bytes written to the output buffer (always `bytes.len() * 2`).
929///
930/// # Panics
931///
932/// Panics if `output.len() < bytes.len() * 2`. The output buffer must be
933/// at least twice the size of the input to hold the hex representation.
934///
935/// # Examples
936///
937/// ```ignore
938/// use osal_rs::utils::bytes_to_hex_into_slice;
939/// 
940/// let data = &[0x01, 0xAB, 0xFF];
941/// let mut buffer = [0u8; 6];
942/// 
943/// let written = bytes_to_hex_into_slice(data, &mut buffer);
944/// assert_eq!(written, 6);
945/// assert_eq!(&buffer, b"01abff");
946/// 
947/// // Will panic - buffer too small
948/// // let mut small = [0u8; 4];
949/// // bytes_to_hex_into_slice(data, &mut small);
950/// ```
951pub fn bytes_to_hex_into_slice(bytes: &[u8], output: &mut [u8]) -> usize {
952    assert!(output.len() >= bytes.len() * 2, "Buffer too small for hex conversion");
953    let mut i = 0;
954    for &b in bytes {
955        let hex = format!("{:02x}", b);
956        output[i..i+2].copy_from_slice(hex.as_bytes());
957        i += 2;
958    }
959    i 
960}
961
962/// Converts a hexadecimal string to a vector of bytes.
963///
964/// Parses a string of hexadecimal digits (case-insensitive) and converts
965/// them to their binary representation. Each pair of hex digits becomes
966/// one byte in the output.
967///
968/// # Parameters
969///
970/// * `hex` - A string slice containing hexadecimal digits (0-9, a-f, A-F)
971///
972/// # Returns
973///
974/// * `Ok(Vec<u8>)` - A vector containing the decoded bytes
975/// * `Err(Error::StringConversionError)` - If the string has odd length or contains invalid hex digits
976///
977/// # Memory Allocation
978///
979/// This function allocates a `Vec` on the heap. For no-alloc environments,
980/// use [`hex_to_bytes_into_slice`] instead.
981///
982/// # Examples
983///
984/// ```ignore
985/// use osal_rs::utils::hex_to_bytes;
986/// 
987/// // Lowercase hex
988/// let bytes = hex_to_bytes("0123abff").unwrap();
989/// assert_eq!(bytes, vec![0x01, 0x23, 0xAB, 0xFF]);
990/// 
991/// // Uppercase hex
992/// let bytes2 = hex_to_bytes("ABCD").unwrap();
993/// assert_eq!(bytes2, vec![0xAB, 0xCD]);
994/// 
995/// // Odd length - error
996/// assert!(hex_to_bytes("ABC").is_err());
997/// 
998/// // Invalid character - error
999/// assert!(hex_to_bytes("0G").is_err());
1000/// ```
1001pub fn hex_to_bytes(hex: &str) -> Result<Vec<u8>> {
1002    if hex.len() % 2 != 0 {
1003        return Err(Error::StringConversionError);
1004    }
1005
1006    let bytes_result: Result<Vec<u8>> = (0..hex.len())
1007        .step_by(2)
1008        .map(|i| {
1009            u8::from_str_radix(&hex[i..i + 2], 16)
1010                .map_err(|_| Error::StringConversionError)
1011        })
1012        .collect();
1013
1014    bytes_result
1015}
1016
1017/// Converts a hexadecimal string to bytes into a pre-allocated buffer.
1018///
1019/// This is a zero-allocation version of [`hex_to_bytes`] that writes decoded
1020/// bytes directly into a provided output buffer. Suitable for embedded systems
1021/// and real-time applications where heap allocation is not desired.
1022///
1023/// # Parameters
1024///
1025/// * `hex` - A string slice containing hexadecimal digits (0-9, a-f, A-F)
1026/// * `output` - The destination buffer to write decoded bytes into
1027///
1028/// # Returns
1029///
1030/// * `Ok(usize)` - The number of bytes written to the output buffer (`hex.len() / 2`)
1031/// * `Err(Error::StringConversionError)` - If:
1032///   - The hex string has odd length
1033///   - The output buffer is too small (`output.len() < hex.len() / 2`)
1034///   - The hex string contains invalid characters
1035///
1036/// # Examples
1037///
1038/// ```ignore
1039/// use osal_rs::utils::hex_to_bytes_into_slice;
1040/// 
1041/// let mut buffer = [0u8; 4];
1042/// let written = hex_to_bytes_into_slice("0123abff", &mut buffer).unwrap();
1043/// assert_eq!(written, 4);
1044/// assert_eq!(buffer, [0x01, 0x23, 0xAB, 0xFF]);
1045/// 
1046/// // Buffer too small
1047/// let mut small = [0u8; 2];
1048/// assert!(hex_to_bytes_into_slice("0123abff", &mut small).is_err());
1049/// 
1050/// // Odd length string
1051/// assert!(hex_to_bytes_into_slice("ABC", &mut buffer).is_err());
1052/// ```
1053pub fn hex_to_bytes_into_slice(hex: &str, output: &mut [u8]) -> Result<usize> {
1054    if hex.len() % 2 != 0 || output.len() < hex.len() / 2 {
1055        return Err(Error::StringConversionError);
1056    }
1057
1058    for i in 0..(hex.len() / 2) {
1059        output[i] = u8::from_str_radix(&hex[2 * i..2 * i + 2], 16)
1060            .map_err(|_| Error::StringConversionError)?;
1061    }
1062
1063    Ok(hex.len() / 2)
1064}