Skip to main content

osal_rs/
utils.rs

1/***************************************************************************
2 *
3 * osal-rs
4 * Copyright (C) 2026 Antonio Salsi <passy.linux@zresa.it>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
18 *
19 ***************************************************************************/
20
21//! Utility types and functions for OSAL-RS.
22//!
23//! This module contains common types, error definitions, and helper functions
24//! used throughout the library.
25//!
26//! # Overview
27//!
28//! The utilities module provides essential building blocks for working with
29//! OSAL-RS in embedded environments:
30//!
31//! - **Error handling**: Comprehensive [`Error`] enum for all OSAL operations
32//! - **String utilities**: Fixed-size [`Bytes`] type for embedded string handling
33//! - **Conversion macros**: Safe C string conversion and parameter extraction
34//! - **FFI types**: Type aliases for C interoperability
35//!
36//! # Main Types
37//!
38//! ## Error Handling
39//!
40//! - [`Error<'a>`] - All possible error conditions with optional borrowed error messages
41//! - [`Result<T, E>`] - Type alias for `core::result::Result` with default `Error<'static>`
42//! - [`OsalRsBool`] - Boolean type compatible with RTOS return values
43//!
44//! ## String Handling
45//!
46//! - [`Bytes<SIZE>`] - Fixed-size byte buffer with string conversion utilities
47//! - [`AsSyncStr`] - Trait for thread-safe string references
48//!
49//! ## Constants
50//!
51//! - [`MAX_DELAY`] - Maximum timeout for blocking indefinitely
52//! - [`CpuRegisterSize`] - CPU register size detection (32-bit or 64-bit)
53//!
54//! ## FFI Types
55//!
56//! - [`Ptr`], [`ConstPtr`], [`DoublePtr`] - Type aliases for C pointers
57//!
58//! # Macros
59//!
60//! ## Parameter Handling
61//!
62//! - [`thread_extract_param!`] - Extract typed parameter from thread entry point
63//! - [`access_static_option!`] - Access static Option variable (panics if None)
64//!
65//! # Helper Functions
66//!
67//! ## Hex Conversion
68//!
69//! - [`bytes_to_hex`] - Convert bytes to hex string (allocates)
70//! - [`bytes_to_hex_into_slice`] - Convert bytes to hex into buffer (no allocation)
71//! - [`hex_to_bytes`] - Parse hex string to bytes (allocates)
72//! - [`hex_to_bytes_into_slice`] - Parse hex string into buffer (no allocation)
73//!
74//! # Platform Detection
75//!
76//! - [`register_bit_size`] - Const function to detect CPU register size (32-bit or 64-bit)
77//!
78//! # Best Practices
79//!
80//! 1. **Use `Bytes<SIZE>` for embedded strings**: Avoids heap allocation, fixed size
81//! 2. **Prefer no-alloc variants**: Use `_into_slice` functions when possible
82//! 3. **Handle errors explicitly**: Always check `Result` returns
83
84use core::ffi::{CStr, c_char, c_uchar, c_void};
85use core::str::{from_utf8_mut, FromStr};
86use core::fmt::{Debug, Display}; 
87use core::ops::{Deref, DerefMut};
88use core::time::Duration;
89
90use alloc::format;
91use alloc::string::{String, ToString};
92use alloc::vec::Vec;
93
94#[cfg(not(feature = "serde"))]
95use crate::os::{Deserialize, Serialize};
96
97#[cfg(feature = "serde")]
98use osal_rs_serde::{Deserialize, Serialize};
99
100/// Error types for OSAL-RS operations.
101///
102/// Represents all possible error conditions that can occur when using
103/// the OSAL-RS library.
104///
105/// # Lifetime Parameter
106///
107/// The error type is generic over lifetime `'a` to allow flexible error messages.
108/// Most of the time, you can use the default [`Result<T>`] type alias which uses
109/// `Error<'static>`. For custom lifetimes in error messages, use
110/// `core::result::Result<T, Error<'a>>` explicitly.
111///
112/// # Examples
113///
114/// ## Basic usage with static errors
115///
116/// ```ignore
117/// use osal_rs::os::{Queue, QueueFn};
118/// use osal_rs::utils::Error;
119/// 
120/// match Queue::new(10, 32) {
121///     Ok(queue) => { /* use queue */ },
122///     Err(Error::OutOfMemory) => println!("Failed to allocate queue"),
123///     Err(e) => println!("Other error: {:?}", e),
124/// }
125/// ```
126///
127/// ## Using borrowed error messages
128///
129/// ```ignore
130/// use osal_rs::utils::Error;
131/// 
132/// fn validate_input(input: &str) -> core::result::Result<(), Error> {
133///     if input.is_empty() {
134///         // Use static lifetime for compile-time strings
135///         Err(Error::Unhandled("Input cannot be empty"))
136///     } else {
137///         Ok(())
138///     }
139/// }
140/// 
141/// // For dynamic error messages from borrowed data
142/// fn process_data<'a>(data: &'a str) -> core::result::Result<(), Error<'a>> {
143///     if !data.starts_with("valid:") {
144///         // Error message borrows from 'data' lifetime
145///         Err(Error::ReadError(data))
146///     } else {
147///         Ok(())
148///     }
149/// }
150/// ```
151#[derive(Debug, Clone, PartialEq, Eq, Hash)]
152pub enum Error<'a> {
153    /// Insufficient memory to complete operation
154    OutOfMemory,
155    /// Queue send operation timed out
156    QueueSendTimeout,
157    /// Queue receive operation timed out
158    QueueReceiveTimeout,
159    /// Mutex operation timed out
160    MutexTimeout,
161    /// Failed to acquire mutex lock
162    MutexLockFailed,
163    /// Generic timeout error
164    Timeout,
165    /// Queue is full and cannot accept more items
166    QueueFull,
167    /// String conversion failed
168    StringConversionError,
169    /// Thread/task not found
170    TaskNotFound,
171    /// Invalid queue size specified
172    InvalidQueueSize,
173    /// Null pointer encountered
174    NullPtr,
175    /// Requested item not found
176    NotFound,
177    /// Index out of bounds
178    OutOfIndex,
179    /// Invalid type for operation
180    InvalidType,
181    /// No data available
182    Empty,
183    /// Write error occurred
184    WriteError(&'a str),
185    /// Read error occurred
186    ReadError(&'a str),
187    /// Return error with code
188    ReturnWithCode(i32),
189    /// Unhandled error with description
190    Unhandled(&'a str),
191    /// Unhandled error with description owned
192    UnhandledOwned(String)
193}
194
195impl<'a> Display for Error<'a> {
196    /// Formats the error for display.
197    ///
198    /// Provides human-readable error messages suitable for logging or
199    /// presentation to users.
200    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
201        use Error::*;
202
203        match self {
204            OutOfMemory => write!(f, "Out of memory"),
205            QueueSendTimeout => write!(f, "Queue send timeout"),
206            QueueReceiveTimeout => write!(f, "Queue receive timeout"),
207            MutexTimeout => write!(f, "Mutex timeout"),
208            MutexLockFailed => write!(f, "Mutex lock failed"),
209            Timeout => write!(f, "Operation timeout"),
210            QueueFull => write!(f, "Queue full"),
211            StringConversionError => write!(f, "String conversion error"),
212            TaskNotFound => write!(f, "Task not found"),
213            InvalidQueueSize => write!(f, "Invalid queue size"),
214            NullPtr => write!(f, "Null pointer encountered"),
215            NotFound => write!(f, "Item not found"),
216            OutOfIndex => write!(f, "Index out of bounds"),
217            InvalidType => write!(f, "Invalid type for operation"),
218            Empty => write!(f, "No data available"),
219            WriteError(desc) => write!(f, "Write error occurred: {}", desc),
220            ReadError(desc) => write!(f, "Read error occurred: {}", desc),
221            ReturnWithCode(code) => write!(f, "Return with code: {}", code),
222            Unhandled(desc) => write!(f, "Unhandled error: {}", desc),
223            UnhandledOwned(desc) => write!(f, "Unhandled error owned: {}", desc),
224        }
225    }
226}
227
228
229/// CPU register size enumeration.
230///
231/// Identifies whether the target CPU uses 32-bit or 64-bit registers.
232/// This is used for platform-specific tick count overflow handling and
233/// time calculation optimizations.
234///
235/// # Usage
236///
237/// Typically determined at compile time via [`register_bit_size()`] which
238/// checks `size_of::<usize>()`.
239///
240/// # Examples
241///
242/// ```ignore
243/// use osal_rs::utils::{CpuRegisterSize, register_bit_size};
244///
245/// match register_bit_size() {
246///     CpuRegisterSize::Bit64 => {
247///         // Use 64-bit optimized calculations
248///     }
249///     CpuRegisterSize::Bit32 => {
250///         // Use 32-bit overflow-safe calculations
251///     }
252/// }
253/// ```
254#[derive(PartialEq, Eq, Clone, Copy, Debug)]
255pub enum CpuRegisterSize {
256    /// 64-bit CPU registers (e.g., ARM Cortex-A, x86_64).
257    ///
258    /// On these platforms, `usize` is 8 bytes.
259    Bit64,
260    
261    /// 32-bit CPU registers (e.g., ARM Cortex-M, RP2040, ESP32).
262    ///
263    /// On these platforms, `usize` is 4 bytes.
264    Bit32
265}
266
267/// Boolean type compatible with RTOS return values.
268///
269/// Many RTOS functions return 0 for success and non-zero for failure.
270/// This type provides a Rust-idiomatic way to work with such values.
271///
272/// # Examples
273///
274/// ```ignore
275/// use osal_rs::os::{Semaphore, SemaphoreFn};
276/// use osal_rs::utils::OsalRsBool;
277/// use core::time::Duration;
278/// 
279/// let sem = Semaphore::new(1, 1).unwrap();
280/// 
281/// match sem.wait(Duration::from_millis(100)) {
282///     OsalRsBool::True => println!("Acquired semaphore"),
283///     OsalRsBool::False => println!("Failed to acquire"),
284/// }
285/// 
286/// // Can also convert to bool
287/// if sem.signal().into() {
288///     println!("Semaphore signaled");
289/// }
290/// ```
291#[derive(PartialEq, Eq, Clone, Copy, Debug)]
292#[repr(u8)]
293pub enum OsalRsBool {
294    /// Operation failed or condition is false
295    False = 1,
296    /// Operation succeeded or condition is true
297    True = 0
298}
299
300/// Maximum delay constant for blocking operations.
301///
302/// When used as a timeout parameter, indicates the operation should
303/// block indefinitely until it succeeds.
304///
305/// # Examples
306///
307/// ```ignore
308/// use osal_rs::os::{Mutex, MutexFn};
309/// use osal_rs::utils::MAX_DELAY;
310/// 
311/// let mutex = Mutex::new(0);
312/// let guard = mutex.lock();  // Blocks forever if needed
313/// ```
314pub const MAX_DELAY: Duration = Duration::from_millis(usize::MAX as u64);
315
316/// Standard Result type for OSAL-RS operations.
317///
318/// Uses [`Error`] as the default error type with `'static` lifetime.
319/// For custom lifetimes, use `core::result::Result<T, Error<'a>>`.
320///
321/// # Examples
322///
323/// ```ignore
324/// use osal_rs::utils::Result;
325///
326/// fn create_resource() -> Result<ResourceHandle> {
327///     // Returns Result<ResourceHandle, Error<'static>>
328///     Ok(ResourceHandle::new())
329/// }
330/// ```
331pub type Result<T, E = Error<'static>> = core::result::Result<T, E>;
332
333/// Pointer to pointer type for C FFI.
334///
335/// Equivalent to `void**` in C. Used for double indirection in FFI calls.
336pub type DoublePtr = *mut *mut c_void;
337
338/// Mutable pointer type for C FFI.
339///
340/// Equivalent to `void*` in C. Used for generic mutable data pointers.
341pub type Ptr = *mut c_void;
342
343/// Const pointer type for C FFI.
344///
345/// Equivalent to `const void*` in C. Used for generic immutable data pointers.
346pub type ConstPtr = *const c_void;
347
348
349/// Determines the CPU register size at compile time.
350///
351/// This constant function checks the size of `usize` to determine whether
352/// the target architecture uses 32-bit or 64-bit registers. This information
353/// is used for platform-specific optimizations and overflow handling.
354///
355/// # Returns
356///
357/// * [`CpuRegisterSize::Bit64`] - For 64-bit architectures
358/// * [`CpuRegisterSize::Bit32`] - For 32-bit architectures
359///
360/// # Examples
361///
362/// ```ignore
363/// use osal_rs::utils::{register_bit_size, CpuRegisterSize};
364/// 
365/// match register_bit_size() {
366///     CpuRegisterSize::Bit64 => println!("Running on 64-bit platform"),
367///     CpuRegisterSize::Bit32 => println!("Running on 32-bit platform"),
368/// }
369/// ```
370pub const fn register_bit_size() -> CpuRegisterSize {
371    if size_of::<usize>() == 8 {
372        CpuRegisterSize::Bit64
373    } else {
374        CpuRegisterSize::Bit32
375    }
376}
377
378
379/// Extracts a typed parameter from an optional boxed Any reference.
380///
381/// This macro is used in thread/task entry points to safely extract and
382/// downcast parameters passed to the thread. It handles both the Option
383/// unwrapping and the type downcast, returning appropriate errors if either
384/// operation fails.
385///
386/// # Parameters
387///
388/// * `$param` - An `Option<Box<dyn Any>>` containing the parameter
389/// * `$t` - The type to downcast the parameter to
390///
391/// # Returns
392///
393/// * A reference to the downcasted value of type `$t`
394/// * `Err(Error::NullPtr)` - If the parameter is None
395/// * `Err(Error::InvalidType)` - If the downcast fails
396///
397/// # Examples
398///
399/// ```ignore
400/// use osal_rs::thread_extract_param;
401/// use osal_rs::utils::Result;
402/// use core::any::Any;
403/// 
404/// struct TaskConfig {
405///     priority: u8,
406///     stack_size: usize,
407/// }
408/// 
409/// fn task_entry(param: Option<Box<dyn Any>>) -> Result<()> {
410///     let config = thread_extract_param!(param, TaskConfig);
411///     
412///     println!("Priority: {}", config.priority);
413///     println!("Stack: {}", config.stack_size);
414///     
415///     Ok(())
416/// }
417/// ```
418#[macro_export]
419macro_rules! thread_extract_param {
420    ($param:expr, $t:ty) => {
421        match $param.as_ref() {
422            Some(p) => {
423                match p.downcast_ref::<$t>() {
424                    Some(value) => value,
425                    None => return Err($crate::utils::Error::InvalidType),
426                }
427            }
428            None => return Err($crate::utils::Error::NullPtr),
429        }
430    };
431}
432
433/// Accesses a static Option variable, returning the contained value or panicking if None.
434/// 
435/// This macro is used to safely access static variables that are initialized at runtime.
436/// It checks if the static variable is `Some` and returns the contained value. If the variable
437/// is `None`, it panics with a message indicating that the variable is not initialized.
438/// 
439/// # Parameters
440/// * `$static_var` - The identifier of the static variable to access
441/// # Returns
442/// * The value contained in the static variable if it is `Some`
443/// * Panics if the static variable is `None`, with a message indicating it is not initialized
444/// # Examples
445/// ```ignore
446/// use osal_rs::access_static_option;
447/// static mut CONFIG: Option<Config> = None;
448/// fn get_config() -> &'static Config {
449///     access_static_option!(CONFIG)
450/// }
451/// ```
452/// 
453/// Note: This macro assumes that the static variable is of type `Option<T>` and that it is initialized at runtime before being accessed. It is intended for use with static variables that are set up during initialization phases of the program, such as in embedded systems where certain resources are not available at compile time.
454/// 
455/// # Safety
456/// This macro uses unsafe code to access the static variable. It is the caller's responsibility to ensure that the static variable is properly initialized before it is accessed, and that it is not accessed concurrently from multiple threads without proper synchronization.
457/// # Warning
458/// This macro will panic if the static variable is not initialized (i.e., if it is `None`). It should be used in contexts where it is guaranteed that the variable will be initialized before
459/// accessing it, such as after an initialization function has been called.
460/// # Alternative
461/// For safer access to static variables, consider using a function that returns a `Result` instead of panicking, allowing the caller to handle the error condition gracefully.
462/// ```ignore
463/// fn get_config() -> Result<&'static Config, Error> {
464///    unsafe {
465///       match &*&raw const CONFIG {
466///         Some(config) => Ok(config),
467///        None => Err(Error::Unhandled("CONFIG is not initialized")),
468///     }
469///  }
470/// }
471/// ```
472/// This alternative approach allows for error handling without panicking, which can be more appropriate in many contexts, especially in production code or libraries where robustness is important.
473/// # Note
474/// This macro is intended for use in embedded systems or low-level code where static variables are commonly used for global state or resources that are initialized at runtime. It provides a convenient way to access such
475/// variables while ensuring that they are initialized, albeit with the risk of panicking if they are not. Use with caution and ensure proper initialization to avoid runtime panics.
476#[macro_export]
477macro_rules! access_static_option {
478    ($static_var:ident) => {
479        unsafe {
480            match &*&raw const $static_var {
481                Some(value) => value,
482                None => panic!(concat!(stringify!($static_var), " is not initialized")),
483            }
484        }
485    };
486}
487
488/// Trait for types that can provide a string reference in a thread-safe manner.
489///
490/// This trait extends the basic string reference functionality with thread-safety
491/// guarantees by requiring both `Sync` and `Send` bounds. It's useful for types
492/// that need to provide string data across thread boundaries in a concurrent
493/// environment.
494///
495/// # Thread Safety
496///
497/// Implementors must be both `Sync` (safe to share references across threads) and
498/// `Send` (safe to transfer ownership across threads).
499///
500/// # Examples
501///
502/// ```ignore
503/// use osal_rs::utils::AsSyncStr;
504/// 
505/// struct ThreadSafeName {
506///     name: &'static str,
507/// }
508/// 
509/// impl AsSyncStr for ThreadSafeName {
510///     fn as_str(&self) -> &str {
511///         self.name
512///     }
513/// }
514/// 
515/// // Can be safely shared across threads
516/// fn use_in_thread(item: &dyn AsSyncStr) {
517///     println!("Name: {}", item.as_str());
518/// }
519/// ```
520pub trait AsSyncStr : Sync + Send { 
521    /// Returns a string slice reference.
522    ///
523    /// This method provides access to the underlying string data in a way
524    /// that is safe to use across thread boundaries.
525    ///
526    /// # Returns
527    ///
528    /// A reference to a string slice with lifetime tied to `self`.
529    fn as_str(&self) -> &str;
530}
531
532impl PartialEq for dyn AsSyncStr + '_ {
533    fn eq(&self, other: &(dyn AsSyncStr + '_)) -> bool {
534        self.as_str() == other.as_str()
535    }
536}
537
538impl Eq for dyn AsSyncStr + '_ {}
539
540impl Debug for dyn AsSyncStr + '_ {
541    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
542        write!(f, "{}", self.as_str())
543    }
544}
545
546impl Display for dyn AsSyncStr + '_ {
547    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
548        write!(f, "{}", self.as_str())
549    }
550}
551
552
553/// Fixed-size byte array wrapper with string conversion utilities.
554///
555/// `Bytes` is a generic wrapper around a fixed-size byte array that provides
556/// convenient methods for converting between strings and byte arrays. It's
557/// particularly useful for interfacing with C APIs that expect fixed-size
558/// character buffers, or for storing strings in embedded systems with
559/// constrained memory.
560///
561/// # Type Parameters
562///
563/// * `SIZE` - The size of the internal byte array (default: 0)
564///
565/// # Examples
566///
567/// ```ignore
568/// use osal_rs::utils::Bytes;
569/// 
570/// // Create an empty 32-byte buffer
571/// let mut buffer = Bytes::<32>::new();
572/// 
573/// // Create a buffer from a string
574/// let name = Bytes::<16>::new_by_str("TaskName");
575/// println!("{}", name); // Prints "TaskName"
576/// 
577/// // Create from any type that implements ToString
578/// let number = 42;
579/// let num_bytes = Bytes::<8>::new_by_string(&number);
580/// ```
581#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
582pub struct Bytes<const SIZE: usize> (pub [u8; SIZE]);
583
584impl<const SIZE: usize> Deref for Bytes<SIZE> {
585    type Target = [u8; SIZE];
586
587    /// Dereferences to the underlying byte array.
588    ///
589    /// This allows `Bytes` to be used anywhere a `[u8; SIZE]` reference is expected.
590    ///
591    /// # Examples
592    ///
593    /// ```ignore
594    /// use osal_rs::utils::Bytes;
595    /// 
596    /// let bytes = Bytes::<8>::new_by_str("test");
597    /// assert_eq!(bytes[0], b't');
598    /// ```
599    fn deref(&self) -> &Self::Target {
600        &self.0
601    }
602}
603
604impl<const SIZE: usize> DerefMut for Bytes<SIZE> {
605    /// Provides mutable access to the underlying byte array.
606    ///
607    /// This allows `Bytes` to be mutably dereferenced, enabling direct modification
608    /// of the internal byte array through the `DerefMut` trait.
609    ///
610    /// # Examples
611    ///
612    /// ```ignore
613    /// use osal_rs::utils::Bytes;
614    /// 
615    /// let mut bytes = Bytes::<8>::new();
616    /// bytes[0] = b'H';
617    /// bytes[1] = b'i';
618    /// assert_eq!(bytes[0], b'H');
619    /// ```
620    fn deref_mut(&mut self) -> &mut Self::Target {
621        &mut self.0
622    }
623}
624
625impl<const SIZE: usize> Display for Bytes<SIZE> {
626    /// Formats the byte array as a C-style null-terminated string.
627    ///
628    /// This implementation treats the byte array as a C string and converts it
629    /// to a Rust string for display. If the conversion fails, it displays
630    /// "Conversion error".
631    ///
632    /// # Safety
633    ///
634    /// This method assumes the byte array contains valid UTF-8 data and is
635    /// null-terminated. Invalid data may result in the error message being displayed.
636    ///
637    /// # Examples
638    ///
639    /// ```ignore
640    /// use osal_rs::utils::Bytes;
641    /// 
642    /// let bytes = Bytes::<16>::new_by_str("Hello");
643    /// println!("{}", bytes); // Prints "Hello"
644    /// ```
645    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
646        let str = unsafe {
647            CStr::from_ptr(self.0.as_ptr() as *const c_char)
648            .to_str()
649            .unwrap_or("Bytes::fmt() Conversion error - invalid UTF-8")
650        };
651        
652        write!(f, "{}", str.to_string())
653    }
654}
655
656impl<const SIZE: usize> FromStr for Bytes<SIZE> {
657    type Err = Error<'static>;
658
659    /// Creates a `Bytes` instance from a string slice.
660    ///
661    /// This implementation allows for easy conversion from string literals or
662    /// string slices to the `Bytes` type, filling the internal byte array
663    /// with the string data and padding with spaces if necessary.
664    ///
665    /// # Examples
666    //// ```ignore
667    /// use osal_rs::utils::Bytes;
668    /// 
669    /// let bytes: Bytes<16> = "Hello".parse().unwrap();
670    /// println!("{}", bytes); // Prints "Hello"
671    /// ```
672    #[inline]
673    fn from_str(s: &str) -> core::result::Result<Self, Self::Err> {
674        Ok(Self::from_str(s))
675    }
676}
677
678impl<const SIZE: usize> From<&str> for Bytes<SIZE> {
679    /// Creates a `Bytes` instance from a string slice.
680    ///
681    /// This implementation allows for easy conversion from string literals or
682    /// string slices to the `Bytes` type, filling the internal byte array
683    /// with the string data and padding with spaces if necessary.
684    ///
685    /// # Examples
686    ///
687    /// ```ignore
688    /// use osal_rs::utils::Bytes;
689    /// 
690    /// let bytes: Bytes<16> = "Hello".into();
691    /// println!("{}", bytes); // Prints "Hello"
692    /// ```
693    #[inline]
694    fn from(s: &str) -> Self {
695        Self::from_str(s)
696    }
697}
698
699impl<const SIZE: usize> core::fmt::Write for Bytes<SIZE> {
700    /// Appends a string slice to the buffer, truncating if the content exceeds `SIZE`.
701    #[inline]
702    fn write_str(&mut self, s: &str) -> core::fmt::Result {
703        self.append_str(s);
704        Ok(())
705    }
706}
707
708impl<const SIZE: usize> AsSyncStr for Bytes<SIZE> {
709    /// Returns a string slice reference.
710    ///
711    /// This method provides access to the underlying string data in a way
712    /// that is safe to use across thread boundaries.
713    ///
714    /// # Returns
715    ///
716    /// A reference to a string slice with lifetime tied to `self`.
717    #[inline]
718    fn as_str(&self) -> &str {
719        self.as_str()
720    }
721}
722
723/// Serialization implementation for `Bytes<SIZE>` when the `serde` feature is enabled.
724///
725/// This implementation provides serialization by directly serializing each byte
726/// in the array using the osal-rs-serde serialization framework.
727#[cfg(feature = "serde")]
728impl<const SIZE: usize> Serialize for Bytes<SIZE> {
729    /// Serializes the `Bytes` instance using the given serializer.
730    ///
731    /// # Parameters
732    ///
733    /// * `serializer` - The serializer to use
734    ///
735    /// # Returns
736    ///
737    /// * `Ok(())` - On successful serialization
738    /// * `Err(S::Error)` - If serialization fails
739    fn serialize<S: osal_rs_serde::Serializer>(&self, name: &str, serializer: &mut S) -> core::result::Result<(), S::Error> {
740        // Find the actual length (up to first null byte or SIZE)
741        let len = self.0.iter().position(|&b| b == 0).unwrap_or(SIZE);
742        
743        // Try to serialize as UTF-8 string if valid, otherwise as hex
744        if let Ok(s) = core::str::from_utf8(&self.0[..len]) {
745            serializer.serialize_str(name, s)
746        } else {
747            // For binary data, serialize as bytes (hex encoded)
748            serializer.serialize_bytes(name, &self.0[..len])
749        }
750    }
751}
752
753/// Deserialization implementation for `Bytes<SIZE>` when the `serde` feature is enabled.
754///
755/// This implementation provides deserialization by reading bytes from the deserializer
756/// into a fixed-size array using the osal-rs-serde deserialization framework.
757#[cfg(feature = "serde")]
758impl<const SIZE: usize> Deserialize for Bytes<SIZE> {
759    /// Deserializes a `Bytes` instance using the given deserializer.
760    ///
761    /// # Parameters
762    ///
763    /// * `deserializer` - The deserializer to use
764    ///
765    /// # Returns
766    ///
767    /// * `Ok(Bytes<SIZE>)` - A new `Bytes` instance with deserialized data
768    /// * `Err(D::Error)` - If deserialization fails
769    fn deserialize<D: osal_rs_serde::Deserializer>(deserializer: &mut D, name: &str) -> core::result::Result<Self, D::Error> {
770        let mut array = [0u8; SIZE];
771        let _ = deserializer.deserialize_bytes(name, &mut array)?;
772        Ok(Self(array))
773    }
774}
775
776/// Serialization implementation for `Bytes<SIZE>` when the `serde` feature is disabled.
777///
778/// This implementation provides basic serialization by directly returning a reference
779/// to the underlying byte array. It's used when the library is compiled without the
780/// `serde` feature, providing a lightweight alternative serialization mechanism.
781#[cfg(not(feature = "serde"))]
782impl<const SIZE: usize> Serialize for Bytes<SIZE> {
783    /// Converts the `Bytes` instance to a byte slice.
784    ///
785    /// # Returns
786    ///
787    /// A reference to the internal byte array.
788    fn to_bytes(&self) -> &[u8] {
789        &self.0
790    }
791}
792
793/// Deserialization implementation for `Bytes<SIZE>` when the `serde` feature is disabled.
794///
795/// This implementation provides basic deserialization by copying bytes from a slice
796/// into a fixed-size array. If the source slice is shorter than `SIZE`, the remaining
797/// bytes are zero-filled. If longer, it's truncated to fit.
798#[cfg(not(feature = "serde"))]
799impl<const SIZE: usize> Deserialize for Bytes<SIZE> {
800    /// Creates a `Bytes` instance from a byte slice.
801    ///
802    /// # Parameters
803    ///
804    /// * `bytes` - The source byte slice to deserialize from
805    ///
806    /// # Returns
807    ///
808    /// * `Ok(Bytes<SIZE>)` - A new `Bytes` instance with data copied from the slice
809    ///
810    /// # Examples
811    ///
812    /// ```ignore
813    /// use osal_rs::utils::Bytes;
814    /// use osal_rs::os::Deserialize;
815    /// 
816    /// let data = b"Hello";
817    /// let bytes = Bytes::<16>::from_bytes(data).unwrap();
818    /// // Result: [b'H', b'e', b'l', b'l', b'o', 0, 0, 0, ...]
819    /// ```
820    fn from_bytes(bytes: &[u8]) -> Result<Self> {
821        let mut array = [0u8; SIZE];
822        let len = core::cmp::min(bytes.len(), SIZE);
823        array[..len].copy_from_slice(&bytes[..len]);
824        Ok(Self( array ))
825    }
826}
827
828
829/// Default implementation for `Bytes<SIZE>`.
830/// This provides a default value for `Bytes<SIZE>`, which is a zero-initialized byte array. This allows `Bytes` to be used in contexts that require a default value, such as when using the `Default` trait or when initializing variables without explicit values.
831/// # Examples
832/// ```ignore
833/// use osal_rs::utils::Bytes;
834/// 
835/// let default_bytes: Bytes<16> = Default::default();
836/// assert_eq!(default_bytes[0], 0);
837/// ```
838/// The default implementation initializes the internal byte array to all zeros, which is a common default state for byte buffers in embedded systems and C APIs. This ensures that any uninitialized `Bytes` instance will contain predictable data (zeros) rather than random memory content.
839/// This is particularly useful when `Bytes` is used as a buffer for C string operations, as it ensures that the buffer starts in a known state. Additionally, it allows for easy creation of empty buffers that can be filled later without needing to manually initialize the array each time.
840/// Overall, this default implementation enhances the usability of the `Bytes` type by providing a sensible default state that is commonly needed in embedded and systems programming contexts.
841/// 
842impl<const SIZE: usize> Default for Bytes<SIZE> {
843    /// Provides a default value for `Bytes<SIZE>`, which is a zero-initialized byte array.
844    ///
845    /// This implementation allows `Bytes` to be used in contexts that require a default value,
846    /// such as when using the `Default` trait or when initializing variables without explicit values.
847    ///
848    /// # Examples
849    ///
850    /// ```ignore
851    /// use osal_rs::utils::Bytes;
852    /// 
853    /// let default_bytes: Bytes<16> = Default::default();
854    /// assert_eq!(default_bytes[0], 0);
855    /// ```
856    fn default() -> Self {
857        Self( [0u8; SIZE] )
858    }
859}
860
861impl<const SIZE: usize> Bytes<SIZE> {
862    /// Creates a new `Bytes` instance filled with zeros.
863    ///
864    /// This is a const function, allowing it to be used in const contexts
865    /// and static variable declarations.
866    ///
867    /// # Returns
868    ///
869    /// A `Bytes` instance with all bytes set to 0.
870    ///
871    /// # Examples
872    ///
873    /// ```ignore
874    /// use osal_rs::utils::Bytes;
875    /// 
876    /// const BUFFER: Bytes<64> = Bytes::new();
877    /// 
878    /// let runtime_buffer = Bytes::<32>::new();
879    /// assert_eq!(runtime_buffer[0], 0);
880    /// ```
881    #[inline]
882    pub const fn new() -> Self {
883        Self( [0u8; SIZE] )
884    }
885
886    /// Creates a new `Bytes` instance from a string slice.
887    ///
888    /// Copies the bytes from the input string into the fixed-size array.
889    /// If the string is shorter than `SIZE`, the remaining bytes are zero-filled.
890    /// If the string is longer, it is truncated to fit.
891    ///
892    /// # Parameters
893    ///
894    /// * `str` - The source string to convert
895    ///
896    /// # Returns
897    ///
898    /// A `Bytes` instance containing the string data.
899    ///
900    /// # Examples
901    ///
902    /// ```ignore
903    /// use osal_rs::utils::Bytes;
904    ///
905    /// let short = Bytes::<16>::new_by_str("Hi");
906    /// // Internal array: [b'H', b'i', 0, 0, 0, ...]
907    ///
908    /// let exact = Bytes::<5>::new_by_str("Hello");
909    /// // Internal array: [b'H', b'e', b'l', b'l', b'o']
910    ///
911    /// let long = Bytes::<3>::new_by_str("Hello");
912    /// // Internal array: [b'H', b'e', b'l'] (truncated)
913    /// ```
914    pub fn from_str(str: &str) -> Self {
915
916        let mut array = [0u8; SIZE];
917
918        let mut i = 0usize ;
919        for byte in str.as_bytes() {
920            if i > SIZE - 1{
921                break;
922            }
923            array[i] = *byte;
924            i += 1;
925        }
926
927        Self( array )
928    }
929
930    /// Creates a new `Bytes` instance from a C string pointer.
931    ///
932    /// Safely converts a null-terminated C string pointer into a `Bytes` instance.
933    /// If the pointer is null, returns a zero-initialized `Bytes`. The function
934    /// copies bytes from the C string into the fixed-size array, truncating if
935    /// the source is longer than `SIZE`.
936    ///
937    /// # Parameters
938    ///
939    /// * `ptr` - A pointer to a null-terminated C string (`*const c_char`)
940    ///
941    /// # Safety
942    ///
943    /// While this function is not marked unsafe, it internally uses `unsafe` code
944    /// to dereference the pointer. The caller must ensure that:
945    /// - If not null, the pointer points to a valid null-terminated C string
946    /// - The memory the pointer references remains valid for the duration of the call
947    ///
948    /// # Returns
949    ///
950    /// A `Bytes` instance containing the C string data, or zero-initialized if the pointer is null.
951    ///
952    /// # Examples
953    ///
954    /// ```ignore
955    /// use osal_rs::utils::Bytes;
956    /// use core::ffi::c_char;
957    /// use alloc::ffi::CString;
958    ///
959    /// // From a CString
960    /// let c_string = CString::new("Hello").unwrap();
961    /// let bytes = Bytes::<16>::new_by_ptr(c_string.as_ptr());
962    ///
963    /// // From a null pointer
964    /// let null_bytes = Bytes::<16>::new_by_ptr(core::ptr::null());
965    /// // Returns zero-initialized Bytes
966    ///
967    /// // Truncation example
968    /// let long_string = CString::new("This is a very long string").unwrap();
969    /// let short_bytes = Bytes::<8>::new_by_ptr(long_string.as_ptr());
970    /// // Only first 8 bytes are copied
971    /// ```
972    pub fn from_char_ptr(ptr: *const c_char) -> Self {
973        if ptr.is_null() {
974            return Self::new();
975        }
976
977        let mut array = [0u8; SIZE];
978
979        let mut i = 0usize ;
980        for byte in unsafe { CStr::from_ptr(ptr) }.to_bytes() {
981            if i > SIZE - 1{
982                break;
983            }
984            array[i] = *byte;
985            i += 1;
986        }
987
988        Self( array )
989    }
990
991
992    /// Creates a new `Bytes` instance from a C unsigned char pointer.
993    /// 
994    /// Safely converts a pointer to an array of unsigned chars into a `Bytes` instance. If the pointer is null, returns a zero-initialized `Bytes`. The function copies bytes from the source pointer into the fixed-size array, truncating if the source is longer than `SIZE`.
995    /// 
996    /// # Parameters
997    /// * `ptr` - A pointer to an array of unsigned chars (`*const c_uchar`)
998    /// 
999    /// # Safety
1000    /// While this function is not marked unsafe, it internally uses `unsafe` code to dereference the pointer. The caller must ensure that:
1001    /// - If not null, the pointer points to a valid array of unsigned chars with at least `SIZE` bytes
1002    /// - The memory the pointer references remains valid for the duration of the call
1003    /// 
1004    /// # Returns
1005    /// A `Bytes` instance containing the data from the source pointer, or zero-initialized if the pointer is null.
1006    /// 
1007    /// # Examples
1008    /// ```ignore
1009    /// use osal_rs::utils::Bytes;
1010    /// use core::ffi::c_uchar;
1011    /// use alloc::ffi::CString;
1012    /// 
1013    /// // From a C unsigned char pointer
1014    /// let data = [b'H', b'e', b'l', b'l', b'o', 0];
1015    /// let bytes = Bytes::<16>::from_uchar_ptr(data.as_ptr());
1016    /// 
1017    /// // From a null pointer
1018    /// let null_bytes = Bytes::<16>::from_uchar_ptr(core::ptr::null());  
1019    /// // Returns zero-initialized Bytes
1020    /// 
1021    /// // Truncation example
1022    /// let long_data = [b'T', b'h', b'i', b's', b' ', b'i', b's', b' ', b'v', b'e', b'r', b'y', b' ', b'l', b'o', b'n', b'g', 0];
1023    /// let short_bytes = Bytes::<8>::from_uchar_ptr(long_data.as_ptr());
1024    /// // Only first 8 bytes are copied
1025    /// ```
1026    pub fn from_uchar_ptr(ptr: *const c_uchar) -> Self {
1027        if ptr.is_null() {
1028            return Self::new();
1029        }
1030
1031        let mut array = [0u8; SIZE];
1032
1033        let mut i = 0usize ;
1034        for byte in unsafe { core::slice::from_raw_parts(ptr, SIZE) } {
1035            if i > SIZE - 1{
1036                break;
1037            }
1038            array[i] = *byte;
1039            i += 1;
1040        }
1041
1042        Self( array )
1043    }
1044
1045    /// Creates a new `Bytes` instance from any type implementing `ToString`.
1046    ///
1047    /// This is a convenience wrapper around [`new_by_str`](Self::new_by_str)
1048    /// that first converts the input to a string.
1049    ///
1050    /// # Parameters
1051    ///
1052    /// * `str` - Any value that implements `ToString`
1053    ///
1054    /// # Returns
1055    ///
1056    /// A `Bytes` instance containing the string representation of the input.
1057    ///
1058    /// # Examples
1059    ///
1060    /// ```ignore
1061    /// use osal_rs::utils::Bytes;
1062    ///
1063    /// // From integer
1064    /// let num_bytes = Bytes::<8>::new_by_string(&42);
1065    ///
1066    /// // From String
1067    /// let string = String::from("Task");
1068    /// let str_bytes = Bytes::<16>::new_by_string(&string);
1069    ///
1070    /// // From custom type with ToString
1071    /// #[derive(Debug)]
1072    /// struct TaskId(u32);
1073    /// impl ToString for TaskId {
1074    ///     fn to_string(&self) -> String {
1075    ///         format!("Task-{}", self.0)
1076    ///     }
1077    /// }
1078    /// let task_bytes = Bytes::<16>::new_by_string(&TaskId(5));
1079    /// ```
1080    #[inline]
1081    pub fn from_as_sync_str(str: &impl ToString) -> Self {
1082        Self::from_str(&str.to_string())
1083    }
1084
1085    /// Creates a new `Bytes` instance from a byte slice.
1086    /// 
1087    /// This function copies bytes from the input slice into the fixed-size array. If the slice is shorter than `SIZE`, the remaining bytes are zero-filled. If the slice is longer, it is truncated to fit.
1088    /// 
1089    /// # Parameters
1090    /// * `bytes` - The source byte slice to convert
1091    /// 
1092    /// # Returns
1093    /// A `Bytes` instance containing the data from the byte slice.
1094    /// 
1095    /// # Examples
1096    /// ```ignore
1097    /// use osal_rs::utils::Bytes;
1098    /// 
1099    /// let data = b"Hello";
1100    /// let bytes = Bytes::<16>::from_bytes(data);
1101    /// // Result: [b'H', b'e', b'l', b'l', b'o', 0, 0, 0, ...]
1102    /// ```
1103    pub fn from_bytes(bytes: &[u8]) -> Self {
1104        let mut array = [0u8; SIZE];
1105        let len = core::cmp::min(bytes.len(), SIZE);
1106        array[..len].copy_from_slice(&bytes[..len]);
1107        Self( array )
1108    }
1109
1110    /// Fills a mutable string slice with the contents of the byte array.
1111    ///
1112    /// Attempts to convert the internal byte array to a UTF-8 string and
1113    /// copies it into the destination string slice. Only copies up to the
1114    /// minimum of the source and destination lengths.
1115    ///
1116    /// # Parameters
1117    ///
1118    /// * `dest` - The destination string slice to fill
1119    ///
1120    /// # Returns
1121    ///
1122    /// `Ok(())` if the operation succeeds, or `Err(Error::StringConversionError)` if the byte array cannot be converted to a valid UTF-8 string.
1123    ///
1124    /// # Examples
1125    ///
1126    /// ```ignore
1127    /// use osal_rs::utils::Bytes;
1128    /// 
1129    /// let bytes = Bytes::<16>::new_by_str("Hello World");
1130    /// 
1131    /// let mut output = String::from("                "); // 16 spaces
1132    /// bytes.fill_str(unsafe { output.as_mut_str() });
1133    /// 
1134    /// assert_eq!(&output[..11], "Hello World");
1135    /// ```
1136    pub fn fill_str(&mut self, dest: &mut str) -> Result<()>{
1137        match from_utf8_mut(&mut self.0) {
1138            Ok(str) => {
1139                let len = core::cmp::min(str.len(), dest.len());
1140                unsafe {
1141                    dest.as_bytes_mut()[..len].copy_from_slice(&str.as_bytes()[..len]);
1142                }
1143                Ok(())
1144            }
1145            Err(_) => Err(Error::StringConversionError),
1146        }
1147    }
1148
1149    /// Creates a new `Bytes` instance from a C string pointer.
1150    ///
1151    /// This is a convenience wrapper around [`new_by_ptr`](Self::new_by_ptr) that directly converts a C string pointer to a `Bytes` instance.
1152    /// If the pointer is null, it returns a zero-initialized `Bytes`. The function copies bytes from the C string into the fixed-size array, truncating if the source is longer than `SIZE`.
1153    ///
1154    /// # Parameters
1155    ///
1156    /// * `str` - A pointer to a null-terminated C string (`*const c_char`)
1157    ///
1158    /// # Safety
1159    ///
1160    /// This method uses `unsafe` code to dereference the pointer. The caller must ensure that:
1161    /// - If not null, the pointer points to a valid null-terminated C string
1162    /// - The memory the pointer references remains valid for the duration of the call
1163    ///
1164    /// - The byte array can be safely interpreted as UTF-8 if the conversion is expected to succeed. If the byte array contains invalid UTF-8, the resulting `Bytes` instance will contain the raw bytes, and the `Display` implementation will show "Conversion error" when attempting to display it as a string.
1165    ///
1166    /// # Returns
1167    ///
1168    /// A `Bytes` instance containing the C string data, or zero-initialized if the pointer is null.
1169    ///
1170    /// # Examples
1171    ///
1172    /// ```ignore
1173    /// use osal_rs::utils::Bytes;
1174    /// use core::ffi::c_char;
1175    /// use alloc::ffi::CString;
1176    /// 
1177    /// // From a CString
1178    /// let c_string = CString::new("Hello").unwrap();
1179    /// let bytes = Bytes::<16>::from_cstr(c_string.as_ptr());
1180    /// 
1181    /// // From a null pointer
1182    /// let null_bytes = Bytes::<16>::from_cstr(core::ptr::null());
1183    /// // Returns zero-initialized Bytes
1184    /// 
1185    /// // Truncation example
1186    /// let long_string = CString::new("This is a very long string").unwrap();
1187    /// let short_bytes = Bytes::<8>::from_cstr(long_string.as_ptr());
1188    /// // Only first 8 bytes are copied
1189    /// ```
1190    #[inline]
1191    pub fn from_cstr(str: *const c_char) -> Self {
1192        Self::from_bytes(unsafe { CStr::from_ptr(str) }.to_bytes())
1193    }
1194
1195    /// Converts the byte array to a C string reference.
1196    ///
1197    /// Creates a `CStr` reference from the internal byte array, treating it as
1198    /// a null-terminated C string. This is useful for passing strings to C FFI
1199    /// functions that expect `*const c_char` or `&CStr`.
1200    ///
1201    /// # Safety
1202    ///
1203    /// This method assumes the byte array is already null-terminated. All
1204    /// constructors (`new()`, `from_str()`, `from_char_ptr()`, etc.) guarantee
1205    /// this property by initializing with `[0u8; SIZE]`.
1206    ///
1207    /// However, if you've manually modified the array via `DerefMut`,
1208    /// you must ensure the last byte remains 0.
1209    ///
1210    /// # Returns
1211    ///
1212    /// A reference to a `CStr` with lifetime tied to `self`.
1213    ///
1214    /// # Examples
1215    ///
1216    /// ```ignore
1217    /// use osal_rs::utils::Bytes;
1218    /// 
1219    /// let bytes = Bytes::<16>::new_by_str("Hello");
1220    /// let c_str = bytes.as_cstr();
1221    /// 
1222    /// extern "C" {
1223    ///     fn print_string(s: *const core::ffi::c_char);
1224    /// }
1225    /// 
1226    /// unsafe {
1227    ///     print_string(c_str.as_ptr());
1228    /// }
1229    /// ```
1230    #[inline]
1231    pub fn as_cstr(&self) -> &CStr {
1232        unsafe {
1233            CStr::from_ptr(self.0.as_ptr() as *const c_char)
1234        }
1235    }
1236
1237    /// Converts the byte array to a C string reference, ensuring null-termination.
1238    ///
1239    /// This is a safer version of `as_cstr()` that explicitly guarantees
1240    /// null-termination by modifying the last byte. Use this if you've
1241    /// manually modified the array and want to ensure it's null-terminated.
1242    ///
1243    /// # Returns
1244    ///
1245    /// A reference to a `CStr` with lifetime tied to `self`.
1246    ///
1247    /// # Examples
1248    ///
1249    /// ```ignore
1250    /// use osal_rs::utils::Bytes;
1251    /// 
1252    /// let mut bytes = Bytes::<16>::new();
1253    /// bytes[0] = b'H';
1254    /// bytes[1] = b'i';
1255    /// // After manual modification, ensure null-termination
1256    /// let c_str = bytes.as_cstr_mut();
1257    /// ```
1258    #[inline]
1259    pub fn as_cstr_mut(&mut self) -> &CStr {
1260        unsafe {
1261            self.0[SIZE - 1] = 0; // Ensure null-termination
1262            CStr::from_ptr(self.0.as_ptr() as *const c_char)
1263        }
1264    }
1265
1266    /// Appends a string slice to the existing content in the `Bytes` buffer.
1267    ///
1268    /// This method finds the current end of the content (first null byte) and appends
1269    /// the provided string starting from that position. If the buffer is already full
1270    /// or if the appended content would exceed the buffer size, the content is truncated
1271    /// to fit within the `SIZE` limit.
1272    ///
1273    /// # Parameters
1274    ///
1275    /// * `str` - The string slice to append
1276    ///
1277    /// # Examples
1278    ///
1279    /// ```
1280    /// use osal_rs::utils::Bytes;
1281    ///
1282    /// let mut bytes = Bytes::<16>::new_by_str("Hello");
1283    /// bytes.append_str(" World");
1284    /// assert_eq!(bytes.as_str(), "Hello World");
1285    ///
1286    /// // Truncation when exceeding buffer size
1287    /// let mut small_bytes = Bytes::<8>::new_by_str("Hi");
1288    /// small_bytes.append_str(" there friend");
1289    /// assert_eq!(small_bytes.as_str(), "Hi ther");
1290    /// ```
1291    pub fn append_str(&mut self, str: &str) {
1292        let current_len = self.0.iter().position(|&b| b == 0).unwrap_or(SIZE);
1293        let mut i = current_len;
1294        for byte in str.as_bytes() {
1295            if i > SIZE - 1{
1296                break;
1297            }
1298            self.0[i] = *byte;
1299            i += 1;
1300        }
1301    }
1302
1303    /// Appends content from any type implementing `AsSyncStr` to the buffer.
1304    ///
1305    /// This method accepts any type that implements the `AsSyncStr` trait, converts
1306    /// it to a string slice, and appends it to the existing content. If the buffer
1307    /// is already full or if the appended content would exceed the buffer size,
1308    /// the content is truncated to fit within the `SIZE` limit.
1309    ///
1310    /// # Parameters
1311    ///
1312    /// * `c_str` - A reference to any type implementing `AsSyncStr`
1313    ///
1314    /// # Examples
1315    ///
1316    /// ```ignore
1317    /// use osal_rs::utils::Bytes;
1318    ///
1319    /// let mut bytes = Bytes::<16>::new_by_str("Hello");
1320    /// let other_bytes = Bytes::<8>::new_by_str(" World");
1321    /// bytes.append_as_sync_str(&other_bytes);
1322    /// assert_eq!(bytes.as_str(), "Hello World");
1323    /// ```
1324    pub fn append_as_sync_str(&mut self, c_str: & impl AsSyncStr) {
1325        let current_len = self.0.iter().position(|&b| b == 0).unwrap_or(SIZE);
1326        let mut i = current_len;
1327        for byte in c_str.as_str().as_bytes() {
1328            if i > SIZE - 1{
1329                break;
1330            }
1331            self.0[i] = *byte;
1332            i += 1;
1333        }
1334    }
1335
1336    /// Appends raw bytes to the existing content in the `Bytes` buffer.
1337    ///
1338    /// This method finds the current end of the content (first null byte) and appends
1339    /// the provided byte slice starting from that position. If the buffer is already
1340    /// full or if the appended content would exceed the buffer size, the content is
1341    /// truncated to fit within the `SIZE` limit.
1342    ///
1343    /// # Parameters
1344    ///
1345    /// * `bytes` - The byte slice to append
1346    ///
1347    /// # Examples
1348    ///
1349    /// ```
1350    /// use osal_rs::utils::Bytes;
1351    ///
1352    /// let mut bytes = Bytes::<16>::new_by_str("Hello");
1353    /// bytes.append_bytes(b" World");
1354    /// assert_eq!(bytes.as_str(), "Hello World");
1355    ///
1356    /// // Appending arbitrary bytes
1357    /// let mut data = Bytes::<16>::new_by_str("Data: ");
1358    /// data.append_bytes(&[0x41, 0x42, 0x43]);
1359    /// assert_eq!(data.as_str(), "Data: ABC");
1360    /// ```
1361    pub fn append_bytes(&mut self, bytes: &[u8]) {
1362        let current_len = self.0.iter().position(|&b| b == 0).unwrap_or(SIZE);
1363        let mut i = current_len;
1364        for byte in bytes {
1365            if i > SIZE - 1{
1366                break;
1367            }
1368            self.0[i] = *byte;
1369            i += 1;
1370        }
1371    }
1372
1373    /// Appends the content of another `Bytes` instance to this buffer.
1374    ///
1375    /// This method allows appending content from a `Bytes` instance of a different
1376    /// size (specified by the generic parameter `OHTER_SIZE`). The method finds the
1377    /// current end of the content (first null byte) and appends the content from the
1378    /// other `Bytes` instance. If the buffer is already full or if the appended content
1379    /// would exceed the buffer size, the content is truncated to fit within the `SIZE` limit.
1380    ///
1381    /// # Type Parameters
1382    ///
1383    /// * `OTHER_SIZE` - The size of the source `Bytes` buffer (can be different from `SIZE`)
1384    ///
1385    /// # Parameters
1386    ///
1387    /// * `other` - A reference to the `Bytes` instance to append
1388    ///
1389    /// # Examples
1390    ///
1391    /// ```
1392    /// use osal_rs::utils::Bytes;
1393    ///
1394    /// let mut bytes = Bytes::<16>::new_by_str("Hello");
1395    /// let other = Bytes::<8>::new_by_str(" World");
1396    /// bytes.append(&other);
1397    /// assert_eq!(bytes.as_str(), "Hello World");
1398    ///
1399    /// // Appending from a larger buffer
1400    /// let mut small = Bytes::<8>::new_by_str("Hi");
1401    /// let large = Bytes::<32>::new_by_str(" there friend");
1402    /// small.append(&large);
1403    /// assert_eq!(small.as_str(), "Hi ther");
1404    /// ```
1405    pub fn append<const OTHER_SIZE: usize>(&mut self, other: &Bytes<OTHER_SIZE>) {
1406        let current_len = self.0.iter().position(|&b| b == 0).unwrap_or(SIZE);
1407        let mut i = current_len;
1408        for &byte in other.0.iter() {
1409            if i > SIZE - 1{
1410                break;
1411            }
1412            self.0[i] = byte;
1413            i += 1;
1414        }
1415    }
1416
1417    /// Clears all content from the buffer, filling it with zeros.
1418    ///
1419    /// This method resets the entire internal byte array to zeros, effectively
1420    /// clearing any stored data. After calling this method, the buffer will be
1421    /// empty and ready for new content.
1422    ///
1423    /// # Examples
1424    ///
1425    /// ```ignore
1426    /// use osal_rs::utils::Bytes;
1427    ///
1428    /// let mut bytes = Bytes::<16>::new_by_str("Hello");
1429    /// assert!(!bytes.is_empty());
1430    ///
1431    /// bytes.clear();
1432    /// assert!(bytes.is_empty());
1433    /// assert_eq!(bytes.len(), 0);
1434    /// ```
1435    pub fn clear(&mut self) {
1436        for byte in self.0.iter_mut() {
1437            *byte = 0;
1438        }
1439    }
1440
1441    /// Returns the length of the content in the buffer.
1442    ///
1443    /// The length is determined by finding the position of the first null byte (0).
1444    /// If no null byte is found, returns `SIZE`, indicating the buffer is completely
1445    /// filled with non-zero data.
1446    ///
1447    /// # Returns
1448    ///
1449    /// The number of bytes before the first null terminator, or `SIZE` if the
1450    /// buffer is completely filled.
1451    ///
1452    /// # Examples
1453    ///
1454    /// ```ignore
1455    /// use osal_rs::utils::Bytes;
1456    ///
1457    /// let bytes = Bytes::<16>::new_by_str("Hello");
1458    /// assert_eq!(bytes.len(), 5);
1459    ///
1460    /// let empty = Bytes::<16>::new();
1461    /// assert_eq!(empty.len(), 0);
1462    ///
1463    /// // Buffer completely filled (no null terminator)
1464    /// let mut full = Bytes::<4>::new();
1465    /// full[0] = b'A';
1466    /// full[1] = b'B';
1467    /// full[2] = b'C';
1468    /// full[3] = b'D';
1469    /// assert_eq!(full.len(), 4);
1470    /// ```
1471    #[inline]
1472    pub fn len(&self) -> usize {
1473        self.0.iter().position(|&b| b == 0).unwrap_or(SIZE)
1474    }
1475
1476    /// Returns a byte slice of the content in the buffer.
1477    /// 
1478    /// This method returns a slice of the internal byte array up to the first null byte (0). If no null byte is found, it returns a slice of the entire array. This allows you to access the valid content stored in the buffer without including any trailing zeros.
1479    /// 
1480    /// # Returns
1481    /// A byte slice containing the content of the buffer up to the first null terminator.
1482    /// 
1483    /// # Examples
1484    /// ```ignore
1485    /// use osal_rs::utils::Bytes;
1486    /// 
1487    /// let bytes = Bytes::<16>::new_by_str("Hello");
1488    /// assert_eq!(bytes.as_raw_bytes(), b"Hello");
1489    /// 
1490    /// let empty = Bytes::<16>::new();
1491    /// assert_eq!(empty.as_raw_bytes(), b"");
1492    /// 
1493    /// let full = Bytes::<4>::new_by_str("ABCD");
1494    /// assert_eq!(full.as_raw_bytes(), b"ABCD");
1495    /// ``` 
1496    #[inline]
1497    pub fn as_raw_bytes(&self) -> &[u8] {
1498        &self.0[..self.len()]
1499    }
1500
1501    /// Returns the fixed size of the buffer.
1502    /// 
1503    /// This method returns the compile-time constant `SIZE`, which represents the total capacity of the internal byte array. The size is determined by the generic parameter `SIZE` specified when creating the `Bytes` instance. This value is fixed and does not change during the lifetime of the instance.
1504    /// # Returns
1505    /// The fixed size of the buffer in bytes (`SIZE`).
1506    /// # Examples
1507    /// ```ignore
1508    /// use osal_rs::utils::Bytes;
1509    /// 
1510    /// let bytes = Bytes::<32>::new();
1511    /// assert_eq!(bytes.size(), 32);
1512    /// 
1513    /// let other = Bytes::<128>::new_by_str("Hello");
1514    /// assert_eq!(other.size(), 128);
1515    /// ```
1516    #[inline]
1517    pub const fn size(&self) -> usize {
1518        SIZE
1519    }
1520
1521    /// Checks if the buffer is empty.
1522    ///
1523    /// A buffer is considered empty if all bytes are zero. This method searches
1524    /// for the first non-zero byte to determine emptiness.
1525    ///
1526    /// # Returns
1527    ///
1528    /// `true` if all bytes are zero, `false` otherwise.
1529    ///
1530    /// # Examples
1531    ///
1532    /// ```ignore
1533    /// use osal_rs::utils::Bytes;
1534    ///
1535    /// let empty = Bytes::<16>::new();
1536    /// assert!(empty.is_empty());
1537    ///
1538    /// let bytes = Bytes::<16>::new_by_str("Hello");
1539    /// assert!(!bytes.is_empty());
1540    ///
1541    /// let mut cleared = Bytes::<16>::new_by_str("Test");
1542    /// cleared.clear();
1543    /// assert!(cleared.is_empty());
1544    /// ```
1545    #[inline]
1546    pub fn is_empty(&self) -> bool {
1547        self.0.iter().position(|&b| b != 0).is_none()
1548    }
1549
1550    /// Returns the total capacity of the buffer.
1551    ///
1552    /// This is the fixed size of the internal byte array, determined at compile
1553    /// time by the generic `SIZE` parameter. The capacity never changes during
1554    /// the lifetime of the `Bytes` instance.
1555    ///
1556    /// # Returns
1557    ///
1558    /// The total capacity in bytes (`SIZE`).
1559    ///
1560    /// # Examples
1561    ///
1562    /// ```ignore
1563    /// use osal_rs::utils::Bytes;
1564    ///
1565    /// let bytes = Bytes::<32>::new();
1566    /// assert_eq!(bytes.capacity(), 32);
1567    ///
1568    /// let other = Bytes::<128>::new_by_str("Hello");
1569    /// assert_eq!(other.capacity(), 128);
1570    /// ```
1571    #[inline]
1572    pub fn capacity(&self) -> usize {
1573        SIZE
1574    }
1575
1576    /// Replaces all occurrences of a byte pattern with another pattern.
1577    ///
1578    /// This method searches for all occurrences of the `find` byte sequence within
1579    /// the buffer and replaces them with the `replace` byte sequence. The replacement
1580    /// is performed in a single pass, and the method handles cases where the replacement
1581    /// is larger, smaller, or equal in size to the pattern being searched for.
1582    ///
1583    /// # Parameters
1584    ///
1585    /// * `find` - The byte pattern to search for
1586    /// * `replace` - The byte pattern to replace with
1587    ///
1588    /// # Returns
1589    ///
1590    /// * `Ok(())` - If all replacements were successful
1591    /// * `Err(Error::StringConversionError)` - If the replacement would exceed the buffer capacity
1592    ///
1593    /// # Behavior
1594    ///
1595    /// - Empty `find` patterns are ignored (returns `Ok(())` immediately)
1596    /// - Multiple occurrences are replaced in a single pass
1597    /// - Content is properly shifted when replacement size differs from find size
1598    /// - Null terminators and trailing bytes are correctly maintained
1599    /// - Overlapping patterns are not re-matched (avoids infinite loops)
1600    ///
1601    /// # Examples
1602    ///
1603    /// ```ignore
1604    /// use osal_rs::utils::Bytes;
1605    ///
1606    /// // Same length replacement
1607    /// let mut bytes = Bytes::<16>::new_by_str("Hello World");
1608    /// bytes.replace(b"World", b"Rust!").unwrap();
1609    /// assert_eq!(bytes.as_str(), "Hello Rust!");
1610    ///
1611    /// // Shorter replacement
1612    /// let mut bytes2 = Bytes::<16>::new_by_str("aabbcc");
1613    /// bytes2.replace(b"bb", b"X").unwrap();
1614    /// assert_eq!(bytes2.as_str(), "aaXcc");
1615    ///
1616    /// // Longer replacement
1617    /// let mut bytes3 = Bytes::<16>::new_by_str("Hi");
1618    /// bytes3.replace(b"Hi", b"Hello").unwrap();
1619    /// assert_eq!(bytes3.as_str(), "Hello");
1620    ///
1621    /// // Multiple occurrences
1622    /// let mut bytes4 = Bytes::<32>::new_by_str("foo bar foo");
1623    /// bytes4.replace(b"foo", b"baz").unwrap();
1624    /// assert_eq!(bytes4.as_str(), "baz bar baz");
1625    ///
1626    /// // Buffer overflow error
1627    /// let mut small = Bytes::<8>::new_by_str("Hello");
1628    /// assert!(small.replace(b"Hello", b"Hello World").is_err());
1629    /// ```
1630    pub fn replace(&mut self, find: &[u8], replace: &[u8]) -> Result<()> {
1631        if find.is_empty() {
1632            return Ok(());
1633        }
1634        
1635        let mut i = 0;
1636        loop {
1637            let current_len = self.len();
1638            
1639            // Exit if we've reached the end
1640            if i >= current_len {
1641                break;
1642            }
1643            
1644            // Check if pattern starts at position i
1645            if i + find.len() <= current_len && self.0[i..i + find.len()] == *find {
1646                let remaining_len = current_len - (i + find.len());
1647                let new_len = i + replace.len() + remaining_len;
1648                
1649                // Check if replacement fits in buffer
1650                if new_len > SIZE {
1651                    return Err(Error::StringConversionError);
1652                }
1653                
1654                // Shift remaining content if sizes differ
1655                if replace.len() != find.len() {
1656                    self.0.copy_within(
1657                        i + find.len()..i + find.len() + remaining_len,
1658                        i + replace.len()
1659                    );
1660                }
1661                
1662                // Insert replacement bytes
1663                self.0[i..i + replace.len()].copy_from_slice(replace);
1664                
1665                // Update null terminator position
1666                if new_len < SIZE {
1667                    self.0[new_len] = 0;
1668                }
1669                
1670                // Clear trailing bytes if content shrunk
1671                if new_len < current_len {
1672                    for j in (new_len + 1)..=current_len {
1673                        if j < SIZE {
1674                            self.0[j] = 0;
1675                        }
1676                    }
1677                }
1678                
1679                // Move past the replacement to avoid infinite loops
1680                i += replace.len();
1681            } else {
1682                i += 1;
1683            }
1684        }
1685        
1686        Ok(())
1687    }
1688
1689    /// Converts the `Bytes` instance to a byte slice.
1690    ///
1691    /// This method provides a convenient way to access the internal byte array
1692    /// as a slice, which can be useful for C FFI or other operations that
1693    /// require byte slices.
1694    ///
1695    /// # Examples
1696    ///
1697    /// ```ignore
1698    /// use osal_rs::utils::{Bytes, ToBytes};
1699    /// 
1700    /// let bytes = Bytes::<8>::new_by_str("example");
1701    /// let byte_slice = bytes.to_bytes();
1702    /// assert_eq!(byte_slice, b"example\0\0");
1703    /// ```
1704    #[inline]
1705    pub fn to_bytes(&self) -> &[u8] {
1706        &self.0
1707    }
1708
1709    /// Pops the last byte from the buffer and returns it.
1710    ///
1711    /// This method removes the last byte of content (before the first null terminator)
1712    /// and returns it. If the buffer is empty, it returns `None`. After popping, the last byte is set to zero to maintain the null-terminated property.
1713    ///
1714    /// # Returns
1715    ///
1716    /// * `Some(u8)` - The last byte of content if the buffer is not empty
1717    /// * `None` - If the buffer is empty
1718    ///
1719    /// # Examples
1720    //// ```ignore
1721    /// use osal_rs::utils::Bytes;
1722    /// 
1723    /// let mut bytes = Bytes::<16>::new_by_str("Hello");
1724    /// assert_eq!(bytes.pop(), Some(b'o'));
1725    /// assert_eq!(bytes.as_str(), "Hell");
1726    /// 
1727    /// // Pop until empty
1728    /// assert_eq!(bytes.pop(), Some(b'l'));
1729    /// assert_eq!(bytes.pop(), Some(b'l'));
1730    /// assert_eq!(bytes.pop(), Some(b'e'));
1731    /// assert_eq!(bytes.pop(), Some(b'H'));
1732    /// assert_eq!(bytes.pop(), None);
1733    /// ``` 
1734    pub fn pop(&mut self) -> Option<u8> {
1735        let len = self.len();
1736        if len == 0 {
1737            None
1738        } else {
1739            let byte = self.0[len - 1];
1740            self.0[len - 1] = 0; // Clear the popped byte
1741            Some(byte)
1742        }
1743    }
1744
1745    /// Pushes a byte to the end of the content in the buffer.
1746    ///
1747    /// # Parameters
1748    ///
1749    /// * `byte` - The byte to push into the buffer
1750    ///
1751    /// # Returns
1752    ///
1753    /// * `Ok(())` - If the byte was successfully pushed
1754    /// * `Err(Error::StringConversionError)` - If the buffer is full
1755    ///
1756    /// # Examples
1757    ///
1758    /// ```ignore
1759    /// use osal_rs::utils::Bytes;
1760    ///
1761    /// let mut bytes = Bytes::<16>::new_by_str("Hello");
1762    /// assert_eq!(bytes.push(b'!'), Ok(()));
1763    /// assert_eq!(bytes.as_str(), "Hello!");
1764    /// ```
1765    pub fn push(&mut self, byte: u8) -> Result<()> {
1766        let len = self.len();
1767        if len >= SIZE {
1768            Err(Error::StringConversionError) // Buffer is full
1769        } else {
1770            self.0[len] = byte;
1771            Ok(())
1772        }
1773    }
1774
1775    /// Pops the last byte from the buffer and returns it as a character.
1776    ///
1777    /// This method removes the last byte of content (before the first null terminator)
1778    /// and attempts to convert it to a `char`. If the buffer is empty or if the byte cannot be converted to a valid `char`, it returns `None`. After popping, the last byte is set to zero to maintain the null-terminated property.
1779    ///
1780    /// # Returns
1781    ///
1782    /// * `Some(char)` - The last byte of content as a character if the buffer is not empty and the byte is a valid character
1783    /// * `None` - If the buffer is empty or if the byte cannot be converted to a valid character
1784    ///
1785    /// # Examples
1786    //// ```ignore
1787    /// use osal_rs::utils::Bytes;
1788    /// 
1789    /// let mut bytes = Bytes::<16>::new_by_str("Hello");
1790    /// assert_eq!(bytes.pop_char(), Some('o'));
1791    /// assert_eq!(bytes.as_str(), "Hell");
1792    /// 
1793    /// // Pop until empty
1794    /// assert_eq!(bytes.pop_char(), Some('l'));
1795    /// assert_eq!(bytes.pop_char(), Some('l'));
1796    /// assert_eq!(bytes.pop_char(), Some('e'));
1797    /// assert_eq!(bytes.pop_char(), Some('H'));
1798    /// assert_eq!(bytes.pop_char(), None);
1799    /// ```
1800    #[inline]
1801    pub fn pop_char(&mut self) -> Option<char> {
1802        self.pop().and_then(|byte| char::from_u32(byte as u32))
1803    }
1804
1805    /// Pushes a character to the end of the content in the buffer.
1806    ///
1807    /// This method attempts to convert the provided `char` to a byte and push it into the buffer. If the character is not a valid ASCII character (i.e., its code point is greater than 127), it returns an error since it cannot be represented as a single byte. If the buffer is full, it also returns an error.
1808    ///
1809    /// # Parameters
1810    ///
1811    /// * `ch` - The character to push into the buffer
1812    ///
1813    /// # Returns
1814    ///
1815    /// * `Ok(())` - If the character was successfully pushed
1816    /// * `Err(Error::StringConversionError)` - If the character is not a valid ASCII character or if the buffer is full
1817    ///
1818    /// # Examples
1819    //// ```ignore
1820    /// use osal_rs::utils::Bytes;
1821    /// 
1822    /// let mut bytes = Bytes::<16>::new_by_str("Hello");
1823    /// assert_eq!(bytes.push_char('!'), Ok(()));
1824    /// assert_eq!(bytes.as_str(), "Hello!");
1825    /// 
1826    /// // Attempt to push a non-ASCII character
1827    /// assert!(bytes.push_char('é').is_err());
1828    /// ```
1829    pub fn push_char(&mut self, ch: char) -> Result<()> {
1830        if ch.is_ascii() {
1831            self.push(ch as u8)
1832        } else {
1833            Err(Error::StringConversionError) // Non-ASCII characters not supported
1834        }
1835    }
1836
1837    /// Checks if the content of the buffer can be interpreted as a valid UTF-8 string.
1838    ///
1839    /// This method attempts to convert the internal byte array to a UTF-8 string. If the conversion is successful, it returns `true`, indicating that the content can be treated as a valid string. If the conversion fails due to invalid UTF-8 sequences, it returns `false`.
1840    ///
1841    /// # Returns
1842    ///
1843    /// * `true` - If the content can be interpreted as a valid UTF-8 string
1844    /// * `false` - If the content contains invalid UTF-8 sequences
1845    ///
1846    /// # Examples
1847    //// ```ignore
1848    /// use osal_rs::utils::Bytes;
1849    /// 
1850    /// let valid_bytes = Bytes::<16>::new_by_str("Hello");
1851    /// assert!(valid_bytes.is_string());
1852    /// 
1853    /// let mut invalid_bytes = Bytes::<16>::new();
1854    /// invalid_bytes[0] = 0xFF; // Invalid UTF-8 byte
1855    /// assert!(!invalid_bytes.is_string());
1856    /// ```
1857    #[inline]
1858    pub fn is_string(&self) -> bool {
1859        String::from_utf8(self.0.to_vec()).is_ok()
1860    }
1861
1862    /// Returns the buffer content as a UTF-8 string slice.
1863    ///
1864    /// Interprets the byte array as a null-terminated C string and returns
1865    /// a `&str`. If the bytes contain invalid UTF-8, returns `"Conversion error"`.
1866    ///
1867    /// This is an inherent method (no trait import required at the call site).
1868    #[inline]
1869    pub fn as_str(&self) -> &str {
1870        unsafe {
1871            CStr::from_ptr(self.0.as_ptr() as *const c_char)
1872                .to_str()
1873                .unwrap_or("Bytes::as_str() Conversion error - invalid UTF-8")
1874        }
1875    }
1876
1877    /// Overwrites the buffer with a formatted string, behaving like `alloc::format!`.
1878    ///
1879    /// Clears the current content and fills the buffer with the result of formatting
1880    /// `args`. Content that exceeds `SIZE` is silently truncated.
1881    ///
1882    /// # Parameters
1883    ///
1884    /// * `args` - A [`core::fmt::Arguments`] value, typically created with [`format_args!`]
1885    ///
1886    /// # Examples
1887    ///
1888    /// ```ignore
1889    /// use osal_rs::utils::Bytes;
1890    ///
1891    /// let mut b = Bytes::<32>::new();
1892    /// b.format(format_args!("Hello {}", 42));
1893    /// assert_eq!(b.as_str(), "Hello 42");
1894    ///
1895    /// let mut b2 = Bytes::<8>::new();
1896    /// b2.format(format_args!("{:.2}", 3.14159));
1897    /// assert_eq!(b2.as_str(), "3.14");
1898    /// ```
1899    #[inline]
1900    pub fn format(&mut self, args: core::fmt::Arguments<'_>) {
1901        self.clear();
1902        let _ = core::fmt::write(self, args);
1903    }
1904        
1905}
1906
1907/// Converts a byte slice to a hexadecimal string representation.
1908///
1909/// Each byte is converted to its two-character hexadecimal representation
1910/// in lowercase. This function allocates a new `String` on the heap.
1911///
1912/// # Parameters
1913///
1914/// * `bytes` - The byte slice to convert
1915///
1916/// # Returns
1917///
1918/// A `String` containing the hexadecimal representation of the bytes.
1919/// Each byte is represented by exactly 2 hex characters (lowercase).
1920///
1921/// # Memory Allocation
1922///
1923/// This function allocates heap memory. In memory-constrained environments,
1924/// consider using [`bytes_to_hex_into_slice`] instead.
1925///
1926/// # Examples
1927///
1928/// ```ignore
1929/// use osal_rs::utils::bytes_to_hex;
1930/// 
1931/// let data = &[0x01, 0x23, 0xAB, 0xFF];
1932/// let hex = bytes_to_hex(data);
1933/// assert_eq!(hex, "0123abff");
1934/// 
1935/// let empty = bytes_to_hex(&[]);
1936/// assert_eq!(empty, "");
1937/// ```
1938#[inline]
1939pub fn bytes_to_hex(bytes: &[u8]) -> String {
1940    bytes.iter()
1941         .map(|b| format!("{:02x}", b))
1942         .collect()
1943}
1944
1945/// Converts a byte slice to hexadecimal representation into a pre-allocated buffer.
1946///
1947/// This is a zero-allocation version of [`bytes_to_hex`] that writes the
1948/// hexadecimal representation directly into a provided output buffer.
1949/// Suitable for embedded systems and real-time applications.
1950///
1951/// # Parameters
1952///
1953/// * `bytes` - The source byte slice to convert
1954/// * `output` - The destination buffer to write hex characters into
1955///
1956/// # Returns
1957///
1958/// The number of bytes written to the output buffer (always `bytes.len() * 2`).
1959///
1960/// # Panics
1961///
1962/// Panics if `output.len() < bytes.len() * 2`. The output buffer must be
1963/// at least twice the size of the input to hold the hex representation.
1964///
1965/// # Examples
1966///
1967/// ```ignore
1968/// use osal_rs::utils::bytes_to_hex_into_slice;
1969/// 
1970/// let data = &[0x01, 0xAB, 0xFF];
1971/// let mut buffer = [0u8; 6];
1972/// 
1973/// let written = bytes_to_hex_into_slice(data, &mut buffer);
1974/// assert_eq!(written, 6);
1975/// assert_eq!(&buffer, b"01abff");
1976/// 
1977/// // Will panic - buffer too small
1978/// // let mut small = [0u8; 4];
1979/// // bytes_to_hex_into_slice(data, &mut small);
1980/// ```
1981pub fn bytes_to_hex_into_slice(bytes: &[u8], output: &mut [u8]) -> usize {
1982    assert!(output.len() >= bytes.len() * 2, "Buffer too small for hex conversion");
1983    let mut i = 0;
1984    for &b in bytes {
1985        let hex = format!("{:02x}", b);
1986        output[i..i+2].copy_from_slice(hex.as_bytes());
1987        i += 2;
1988    }
1989    i 
1990}
1991
1992/// Converts a hexadecimal string to a vector of bytes.
1993///
1994/// Parses a string of hexadecimal digits (case-insensitive) and converts
1995/// them to their binary representation. Each pair of hex digits becomes
1996/// one byte in the output.
1997///
1998/// # Parameters
1999///
2000/// * `hex` - A string slice containing hexadecimal digits (0-9, a-f, A-F)
2001///
2002/// # Returns
2003///
2004/// * `Ok(Vec<u8>)` - A vector containing the decoded bytes
2005/// * `Err(Error::StringConversionError)` - If the string has odd length or contains invalid hex digits
2006///
2007/// # Memory Allocation
2008///
2009/// This function allocates a `Vec` on the heap. For no-alloc environments,
2010/// use [`hex_to_bytes_into_slice`] instead.
2011///
2012/// # Examples
2013///
2014/// ```ignore
2015/// use osal_rs::utils::hex_to_bytes;
2016/// 
2017/// // Lowercase hex
2018/// let bytes = hex_to_bytes("0123abff").unwrap();
2019/// assert_eq!(bytes, vec![0x01, 0x23, 0xAB, 0xFF]);
2020/// 
2021/// // Uppercase hex
2022/// let bytes2 = hex_to_bytes("ABCD").unwrap();
2023/// assert_eq!(bytes2, vec![0xAB, 0xCD]);
2024/// 
2025/// // Odd length - error
2026/// assert!(hex_to_bytes("ABC").is_err());
2027/// 
2028/// // Invalid character - error
2029/// assert!(hex_to_bytes("0G").is_err());
2030/// ```
2031pub fn hex_to_bytes(hex: &str) -> Result<Vec<u8>> {
2032    if hex.len() % 2 != 0 {
2033        return Err(Error::StringConversionError);
2034    }
2035
2036    let bytes_result: Result<Vec<u8>> = (0..hex.len())
2037        .step_by(2)
2038        .map(|i| {
2039            u8::from_str_radix(&hex[i..i + 2], 16)
2040                .map_err(|_| Error::StringConversionError)
2041        })
2042        .collect();
2043
2044    bytes_result
2045}
2046
2047/// Converts a hexadecimal string to bytes into a pre-allocated buffer.
2048///
2049/// This is a zero-allocation version of [`hex_to_bytes`] that writes decoded
2050/// bytes directly into a provided output buffer. Suitable for embedded systems
2051/// and real-time applications where heap allocation is not desired.
2052///
2053/// # Parameters
2054///
2055/// * `hex` - A string slice containing hexadecimal digits (0-9, a-f, A-F)
2056/// * `output` - The destination buffer to write decoded bytes into
2057///
2058/// # Returns
2059///
2060/// * `Ok(usize)` - The number of bytes written to the output buffer (`hex.len() / 2`)
2061/// * `Err(Error::StringConversionError)` - If:
2062///   - The hex string has odd length
2063///   - The output buffer is too small (`output.len() < hex.len() / 2`)
2064///   - The hex string contains invalid characters
2065///
2066/// # Examples
2067///
2068/// ```ignore
2069/// use osal_rs::utils::hex_to_bytes_into_slice;
2070/// 
2071/// let mut buffer = [0u8; 4];
2072/// let written = hex_to_bytes_into_slice("0123abff", &mut buffer).unwrap();
2073/// assert_eq!(written, 4);
2074/// assert_eq!(buffer, [0x01, 0x23, 0xAB, 0xFF]);
2075/// 
2076/// // Buffer too small
2077/// let mut small = [0u8; 2];
2078/// assert!(hex_to_bytes_into_slice("0123abff", &mut small).is_err());
2079/// 
2080/// // Odd length string
2081/// assert!(hex_to_bytes_into_slice("ABC", &mut buffer).is_err());
2082/// ```
2083pub fn hex_to_bytes_into_slice(hex: &str, output: &mut [u8]) -> Result<usize> {
2084    if hex.len() % 2 != 0 || output.len() < hex.len() / 2 {
2085        return Err(Error::StringConversionError);
2086    }
2087
2088    for i in 0..(hex.len() / 2) {
2089        output[i] = u8::from_str_radix(&hex[2 * i..2 * i + 2], 16)
2090            .map_err(|_| Error::StringConversionError)?;
2091    }
2092
2093    Ok(hex.len() / 2)
2094}