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