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::{FromStr, from_utf8, from_utf8_mut};
86use core::fmt::{Arguments, Debug, Display, Formatter, Write, write}; 
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/// ```
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/// ```
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 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/// ```
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/// ```
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/// match sem.signal() {
287///     OsalRsBool::True => println!("Semaphore signaled"),
288///     OsalRsBool::False => println!("Failed to signal"),
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/// ```
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/// ```
324/// use osal_rs::utils::Result;
325///
326/// struct ResourceHandle;
327///
328/// fn create_resource() -> Result<ResourceHandle> {
329///     // Returns Result<ResourceHandle, Error<'static>>
330///     Ok(ResourceHandle)
331/// }
332///
333/// assert!(create_resource().is_ok());
334/// ```
335pub type Result<T, E = Error<'static>> = core::result::Result<T, E>;
336
337/// Pointer to pointer type for C FFI.
338///
339/// Equivalent to `void**` in C. Used for double indirection in FFI calls.
340pub type DoublePtr = *mut *mut c_void;
341
342/// Mutable pointer type for C FFI.
343///
344/// Equivalent to `void*` in C. Used for generic mutable data pointers.
345pub type Ptr = *mut c_void;
346
347/// Const pointer type for C FFI.
348///
349/// Equivalent to `const void*` in C. Used for generic immutable data pointers.
350pub type ConstPtr = *const c_void;
351
352
353/// Determines the CPU register size at compile time.
354///
355/// This constant function checks the size of `usize` to determine whether
356/// the target architecture uses 32-bit or 64-bit registers. This information
357/// is used for platform-specific optimizations and overflow handling.
358///
359/// # Returns
360///
361/// * [`CpuRegisterSize::Bit64`] - For 64-bit architectures
362/// * [`CpuRegisterSize::Bit32`] - For 32-bit architectures
363///
364/// # Examples
365///
366/// ```
367/// use osal_rs::utils::{register_bit_size, CpuRegisterSize};
368/// 
369/// match register_bit_size() {
370///     CpuRegisterSize::Bit64 => println!("Running on 64-bit platform"),
371///     CpuRegisterSize::Bit32 => println!("Running on 32-bit platform"),
372/// }
373/// ```
374pub const fn register_bit_size() -> CpuRegisterSize {
375    if size_of::<usize>() == 8 {
376        CpuRegisterSize::Bit64
377    } else {
378        CpuRegisterSize::Bit32
379    }
380}
381
382
383/// Extracts a typed parameter from an optional boxed Any reference.
384///
385/// This macro is used in thread/task entry points to safely extract and
386/// downcast parameters passed to the thread. It handles both the Option
387/// unwrapping and the type downcast, returning appropriate errors if either
388/// operation fails.
389///
390/// # Parameters
391///
392/// * `$param` - An `Option<Box<dyn Any>>` containing the parameter
393/// * `$t` - The type to downcast the parameter to
394///
395/// # Returns
396///
397/// * A reference to the downcasted value of type `$t`
398/// * `Err(Error::NullPtr)` - If the parameter is None
399/// * `Err(Error::InvalidType)` - If the downcast fails
400///
401/// # Examples
402///
403/// ```
404/// use osal_rs::thread_extract_param;
405/// use osal_rs::utils::Result;
406/// use core::any::Any;
407/// 
408/// struct TaskConfig {
409///     priority: u8,
410///     stack_size: usize,
411/// }
412/// 
413/// fn task_entry(param: Option<Box<dyn Any>>) -> Result<()> {
414///     let config = thread_extract_param!(param, TaskConfig);
415///     
416///     println!("Priority: {}", config.priority);
417///     println!("Stack: {}", config.stack_size);
418///     
419///     Ok(())
420/// }
421/// ```
422#[macro_export]
423macro_rules! thread_extract_param {
424    ($param:expr, $t:ty) => {{
425        let Some(p) = $param.as_ref() else {
426            return Err($crate::utils::Error::NullPtr);
427        };
428        let Some(value) = p.downcast_ref::<$t>() else {
429            return Err($crate::utils::Error::InvalidType);
430        };
431        value
432    }};
433}
434
435/// Accesses a static Option variable, returning the contained value or panicking if None.
436/// 
437/// This macro is used to safely access static variables that are initialized at runtime.
438/// It checks if the static variable is `Some` and returns the contained value. If the variable
439/// is `None`, it panics with a message indicating that the variable is not initialized.
440/// 
441/// # Parameters
442/// * `$static_var` - The identifier of the static variable to access
443/// # Returns
444/// * The value contained in the static variable if it is `Some`
445/// * Panics if the static variable is `None`, with a message indicating it is not initialized
446/// # Examples
447/// ```
448/// use osal_rs::access_static_option;
449///
450/// struct Config;
451///
452/// static mut CONFIG: Option<Config> = Some(Config);
453///
454/// fn get_config() -> &'static Config {
455///     access_static_option!(CONFIG)
456/// }
457///
458/// get_config();
459/// ```
460/// 
461/// 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.
462/// 
463/// # Safety
464/// 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.
465/// # Warning
466/// 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
467/// accessing it, such as after an initialization function has been called.
468/// # Alternative
469/// 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.
470/// ```
471/// use osal_rs::utils::{Error, Result};
472///
473/// struct Config;
474///
475/// static mut CONFIG: Option<Config> = Some(Config);
476///
477/// fn get_config() -> Result<&'static Config> {
478///     unsafe {
479///         match &*&raw const CONFIG {
480///             Some(config) => Ok(config),
481///             None => Err(Error::Unhandled("CONFIG is not initialized")),
482///         }
483///     }
484/// }
485///
486/// assert!(get_config().is_ok());
487/// ```
488/// 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.
489/// # Note
490/// 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
491/// 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.
492#[macro_export]
493macro_rules! access_static_option {
494    ($static_var:ident) => {
495        unsafe {
496            match &*&raw const $static_var {
497                Some(value) => value,
498                None => panic!(concat!(stringify!($static_var), " is not initialized")),
499            }
500        }
501    };
502}
503
504/// Trait for types that can provide a string reference in a thread-safe manner.
505///
506/// This trait extends the basic string reference functionality with thread-safety
507/// guarantees by requiring both `Sync` and `Send` bounds. It's useful for types
508/// that need to provide string data across thread boundaries in a concurrent
509/// environment.
510///
511/// # Thread Safety
512///
513/// Implementors must be both `Sync` (safe to share references across threads) and
514/// `Send` (safe to transfer ownership across threads).
515///
516/// # Examples
517///
518/// ```
519/// use osal_rs::utils::AsSyncStr;
520/// 
521/// struct ThreadSafeName {
522///     name: &'static str,
523/// }
524/// 
525/// impl AsSyncStr for ThreadSafeName {
526///     fn as_str(&self) -> &str {
527///         self.name
528///     }
529/// }
530/// 
531/// // Can be safely shared across threads
532/// fn use_in_thread(item: &dyn AsSyncStr) {
533///     println!("Name: {}", item.as_str());
534/// }
535/// ```
536pub trait AsSyncStr : Sync + Send { 
537    /// Returns a string slice reference.
538    ///
539    /// This method provides access to the underlying string data in a way
540    /// that is safe to use across thread boundaries.
541    ///
542    /// # Returns
543    ///
544    /// A reference to a string slice with lifetime tied to `self`.
545    fn as_str(&self) -> &str;
546}
547
548impl PartialEq for dyn AsSyncStr + '_ {
549    fn eq(&self, other: &(dyn AsSyncStr + '_)) -> bool {
550        self.as_str() == other.as_str()
551    }
552}
553
554impl Eq for dyn AsSyncStr + '_ {}
555
556impl Debug for dyn AsSyncStr + '_ {
557    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
558        write!(f, "{}", self.as_str())
559    }
560}
561
562impl Display for dyn AsSyncStr + '_ {
563    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
564        write!(f, "{}", self.as_str())
565    }
566}
567
568
569/// Fixed-size byte array wrapper with string conversion utilities.
570///
571/// `Bytes` is a generic wrapper around a fixed-size byte array that provides
572/// convenient methods for converting between strings and byte arrays. It's
573/// particularly useful for interfacing with C APIs that expect fixed-size
574/// character buffers, or for storing strings in embedded systems with
575/// constrained memory.
576///
577/// # Type Parameters
578///
579/// * `SIZE` - The size of the internal byte array (default: 0)
580///
581/// # Examples
582///
583/// ```
584/// use osal_rs::utils::Bytes;
585/// 
586/// // Create an empty 32-byte buffer
587/// let mut buffer = Bytes::<32>::new();
588/// 
589/// // Create a buffer from a string
590/// let name = Bytes::<16>::from_str("TaskName");
591/// println!("{}", name); // Prints "TaskName"
592/// 
593/// // Create from any type that implements ToString
594/// let number = 42;
595/// let num_bytes = Bytes::<8>::from_as_sync_str(&number);
596/// ```
597#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
598pub struct Bytes<const SIZE: usize> (pub [u8; SIZE]);
599
600impl<const SIZE: usize> Deref for Bytes<SIZE> {
601    type Target = [u8; SIZE];
602
603    /// Dereferences to the underlying byte array.
604    ///
605    /// This allows `Bytes` to be used anywhere a `[u8; SIZE]` reference is expected.
606    ///
607    /// # Examples
608    ///
609    /// ```
610    /// use osal_rs::utils::Bytes;
611    /// 
612    /// let bytes = Bytes::<8>::from_str("test");
613    /// assert_eq!(bytes[0], b't');
614    /// ```
615    fn deref(&self) -> &Self::Target {
616        &self.0
617    }
618}
619
620impl<const SIZE: usize> DerefMut for Bytes<SIZE> {
621    /// Provides mutable access to the underlying byte array.
622    ///
623    /// This allows `Bytes` to be mutably dereferenced, enabling direct modification
624    /// of the internal byte array through the `DerefMut` trait.
625    ///
626    /// # Examples
627    ///
628    /// ```
629    /// use osal_rs::utils::Bytes;
630    /// 
631    /// let mut bytes = Bytes::<8>::new();
632    /// bytes[0] = b'H';
633    /// bytes[1] = b'i';
634    /// assert_eq!(bytes[0], b'H');
635    /// ```
636    fn deref_mut(&mut self) -> &mut Self::Target {
637        &mut self.0
638    }
639}
640
641impl<const SIZE: usize> Display for Bytes<SIZE> {
642    /// Formats the byte array as a C-style null-terminated string.
643    ///
644    /// This implementation treats the byte array as a C string and converts it
645    /// to a Rust string for display. If the conversion fails, it displays
646    /// "Conversion error".
647    ///
648    /// # Safety
649    ///
650    /// This method assumes the byte array contains valid UTF-8 data and is
651    /// null-terminated. Invalid data may result in the error message being displayed.
652    ///
653    /// # Examples
654    ///
655    /// ```
656    /// use osal_rs::utils::Bytes;
657    /// 
658    /// let bytes = Bytes::<16>::from_str("Hello");
659    /// println!("{}", bytes); // Prints "Hello"
660    /// ```
661    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
662        let str = unsafe {
663            CStr::from_ptr(self.0.as_ptr() as *const c_char)
664            .to_str()
665            .unwrap_or("Bytes::fmt() Conversion error - invalid UTF-8")
666        };
667        
668        write!(f, "{}", str.to_string())
669    }
670}
671
672impl<const SIZE: usize> FromStr for Bytes<SIZE> {
673    type Err = Error<'static>;
674
675    /// Creates a `Bytes` instance from a string slice.
676    ///
677    /// This implementation allows for easy conversion from string literals or
678    /// string slices to the `Bytes` type, filling the internal byte array
679    /// with the string data and padding with spaces if necessary.
680    ///
681    /// # Examples
682    /// ```
683    /// use osal_rs::utils::Bytes;
684    ///
685    /// let bytes: Bytes<16> = "Hello".parse().unwrap();
686    /// println!("{}", bytes); // Prints "Hello"
687    /// ```
688    #[inline]
689    fn from_str(s: &str) -> core::result::Result<Self, Self::Err> {
690        Ok(Self::from_str(s))
691    }
692}
693
694impl<const SIZE: usize> From<&str> for Bytes<SIZE> {
695    /// Creates a `Bytes` instance from a string slice.
696    ///
697    /// This implementation allows for easy conversion from string literals or
698    /// string slices to the `Bytes` type, filling the internal byte array
699    /// with the string data and padding with spaces if necessary.
700    ///
701    /// # Examples
702    ///
703    /// ```
704    /// use osal_rs::utils::Bytes;
705    /// 
706    /// let bytes: Bytes<16> = "Hello".into();
707    /// println!("{}", bytes); // Prints "Hello"
708    /// ```
709    #[inline]
710    fn from(s: &str) -> Self {
711        Self::from_str(s)
712    }
713}
714
715impl<const SIZE: usize> Write for Bytes<SIZE> {
716    /// Appends a string slice to the buffer, truncating if the content exceeds `SIZE`.
717    #[inline]
718    fn write_str(&mut self, s: &str) -> core::fmt::Result {
719        self.append_str(s);
720        Ok(())
721    }
722}
723
724impl<const SIZE: usize> AsSyncStr for Bytes<SIZE> {
725    /// Returns a string slice reference.
726    ///
727    /// This method provides access to the underlying string data in a way
728    /// that is safe to use across thread boundaries.
729    ///
730    /// # Returns
731    ///
732    /// A reference to a string slice with lifetime tied to `self`.
733    #[inline]
734    fn as_str(&self) -> &str {
735        self.as_str()
736    }
737}
738
739/// Serialization implementation for `Bytes<SIZE>` when the `serde` feature is enabled.
740///
741/// This implementation provides serialization by directly serializing each byte
742/// in the array using the osal-rs-serde serialization framework.
743#[cfg(feature = "serde")]
744impl<const SIZE: usize> Serialize for Bytes<SIZE> {
745    /// Serializes the `Bytes` instance using the given serializer.
746    ///
747    /// # Parameters
748    ///
749    /// * `serializer` - The serializer to use
750    ///
751    /// # Returns
752    ///
753    /// * `Ok(())` - On successful serialization
754    /// * `Err(S::Error)` - If serialization fails
755    fn serialize<S: osal_rs_serde::Serializer>(&self, name: &str, serializer: &mut S) -> core::result::Result<(), S::Error> {
756        // Find the actual length (up to first null byte or SIZE)
757        let len = self.0.iter().position(|&b| b == 0).unwrap_or(SIZE);
758        
759        // Try to serialize as UTF-8 string if valid, otherwise as hex
760        if let Ok(s) = core::str::from_utf8(&self.0[..len]) {
761            serializer.serialize_str(name, s)
762        } else {
763            // For binary data, serialize as bytes (hex encoded)
764            serializer.serialize_bytes(name, &self.0[..len])
765        }
766    }
767}
768
769/// Deserialization implementation for `Bytes<SIZE>` when the `serde` feature is enabled.
770///
771/// This implementation provides deserialization by reading bytes from the deserializer
772/// into a fixed-size array using the osal-rs-serde deserialization framework.
773#[cfg(feature = "serde")]
774impl<const SIZE: usize> Deserialize for Bytes<SIZE> {
775    /// Deserializes a `Bytes` instance using the given deserializer.
776    ///
777    /// # Parameters
778    ///
779    /// * `deserializer` - The deserializer to use
780    ///
781    /// # Returns
782    ///
783    /// * `Ok(Bytes<SIZE>)` - A new `Bytes` instance with deserialized data
784    /// * `Err(D::Error)` - If deserialization fails
785    fn deserialize<D: osal_rs_serde::Deserializer>(deserializer: &mut D, name: &str) -> core::result::Result<Self, D::Error> {
786        let mut array = [0u8; SIZE];
787        let _ = deserializer.deserialize_bytes(name, &mut array)?;
788        Ok(Self(array))
789    }
790}
791
792/// Serialization implementation for `Bytes<SIZE>` when the `serde` feature is disabled.
793///
794/// This implementation provides basic serialization by directly returning a reference
795/// to the underlying byte array. It's used when the library is compiled without the
796/// `serde` feature, providing a lightweight alternative serialization mechanism.
797#[cfg(not(feature = "serde"))]
798impl<const SIZE: usize> Serialize for Bytes<SIZE> {
799    /// Converts the `Bytes` instance to a byte slice.
800    ///
801    /// # Returns
802    ///
803    /// A reference to the internal byte array.
804    #[inline]
805    fn to_bytes(&self) -> &[u8] {
806        &self.0
807    }
808}
809
810/// Deserialization implementation for `Bytes<SIZE>` when the `serde` feature is disabled.
811///
812/// This implementation provides basic deserialization by copying bytes from a slice
813/// into a fixed-size array. If the source slice is shorter than `SIZE`, the remaining
814/// bytes are zero-filled. If longer, it's truncated to fit.
815#[cfg(not(feature = "serde"))]
816impl<const SIZE: usize> Deserialize for Bytes<SIZE> {
817    /// Creates a `Bytes` instance from a byte slice.
818    ///
819    /// # Parameters
820    ///
821    /// * `bytes` - The source byte slice to deserialize from
822    ///
823    /// # Returns
824    ///
825    /// * `Ok(Bytes<SIZE>)` - A new `Bytes` instance with data copied from the slice
826    ///
827    /// # Examples
828    ///
829    /// ```
830    /// use osal_rs::utils::Bytes;
831    /// use osal_rs::os::Deserialize;
832    ///
833    /// let data = b"Hello";
834    /// let bytes = <Bytes<16> as Deserialize>::from_bytes(data).unwrap();
835    /// // Result: [b'H', b'e', b'l', b'l', b'o', 0, 0, 0, ...]
836    /// assert_eq!(bytes.as_str(), "Hello");
837    /// ```
838    fn from_bytes(bytes: &[u8]) -> Result<Self> {
839        let mut array = [0u8; SIZE];
840        let len = core::cmp::min(bytes.len(), SIZE);
841        array[..len].copy_from_slice(&bytes[..len]);
842        Ok(Self( array ))
843    }
844}
845
846
847/// Default implementation for `Bytes<SIZE>`.
848/// 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.
849/// # Examples
850/// ```
851/// use osal_rs::utils::Bytes;
852/// 
853/// let default_bytes: Bytes<16> = Default::default();
854/// assert_eq!(default_bytes[0], 0);
855/// ```
856/// 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.
857/// 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.
858/// 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.
859/// 
860impl<const SIZE: usize> Default for Bytes<SIZE> {
861    /// Provides a default value for `Bytes<SIZE>`, which is a zero-initialized byte array.
862    ///
863    /// This implementation allows `Bytes` to be used in contexts that require a default value,
864    /// such as when using the `Default` trait or when initializing variables without explicit values.
865    ///
866    /// # Examples
867    ///
868    /// ```
869    /// use osal_rs::utils::Bytes;
870    /// 
871    /// let default_bytes: Bytes<16> = Default::default();
872    /// assert_eq!(default_bytes[0], 0);
873    /// ```
874    fn default() -> Self {
875        Self( [0u8; SIZE] )
876    }
877}
878
879impl<const SIZE: usize> Bytes<SIZE> {
880    /// Creates a new `Bytes` instance filled with zeros.
881    ///
882    /// This is a const function, allowing it to be used in const contexts
883    /// and static variable declarations.
884    ///
885    /// # Returns
886    ///
887    /// A `Bytes` instance with all bytes set to 0.
888    ///
889    /// # Examples
890    ///
891    /// ```
892    /// use osal_rs::utils::Bytes;
893    /// 
894    /// const BUFFER: Bytes<64> = Bytes::new();
895    /// 
896    /// let runtime_buffer = Bytes::<32>::new();
897    /// assert_eq!(runtime_buffer[0], 0);
898    /// ```
899    #[inline]
900    pub const fn new() -> Self {
901        Self( [0u8; SIZE] )
902    }
903
904    /// Creates a new `Bytes` instance from a string slice.
905    ///
906    /// Copies the bytes from the input string into the fixed-size array.
907    /// If the string is shorter than `SIZE`, the remaining bytes are zero-filled.
908    /// If the string is longer, it is truncated to fit.
909    ///
910    /// # Parameters
911    ///
912    /// * `str` - The source string to convert
913    ///
914    /// # Returns
915    ///
916    /// A `Bytes` instance containing the string data.
917    ///
918    /// # Examples
919    ///
920    /// ```
921    /// use osal_rs::utils::Bytes;
922    ///
923    /// let short = Bytes::<16>::from_str("Hi");
924    /// // Internal array: [b'H', b'i', 0, 0, 0, ...]
925    ///
926    /// let exact = Bytes::<5>::from_str("Hello");
927    /// // Internal array: [b'H', b'e', b'l', b'l', b'o']
928    ///
929    /// let long = Bytes::<3>::from_str("Hello");
930    /// // Internal array: [b'H', b'e', b'l'] (truncated)
931    /// ```
932    pub fn from_str(str: &str) -> Self {
933
934        let mut array = [0u8; SIZE];
935
936        let mut i = 0usize ;
937        for byte in str.as_bytes() {
938            if i > SIZE - 1{
939                break;
940            }
941            array[i] = *byte;
942            i += 1;
943        }
944
945        Self( array )
946    }
947
948    /// Creates a new `Bytes` instance from a C string pointer.
949    ///
950    /// Safely converts a null-terminated C string pointer into a `Bytes` instance.
951    /// If the pointer is null, returns a zero-initialized `Bytes`. The function
952    /// copies bytes from the C string into the fixed-size array, truncating if
953    /// the source is longer than `SIZE`.
954    ///
955    /// # Parameters
956    ///
957    /// * `ptr` - A pointer to a null-terminated C string (`*const c_char`)
958    ///
959    /// # Safety
960    ///
961    /// While this function is not marked unsafe, it internally uses `unsafe` code
962    /// to dereference the pointer. The caller must ensure that:
963    /// - If not null, the pointer points to a valid null-terminated C string
964    /// - The memory the pointer references remains valid for the duration of the call
965    ///
966    /// # Returns
967    ///
968    /// A `Bytes` instance containing the C string data, or zero-initialized if the pointer is null.
969    ///
970    /// # Examples
971    ///
972    /// ```
973    /// use osal_rs::utils::Bytes;
974    /// use std::ffi::CString;
975    ///
976    /// // From a CString
977    /// let c_string = CString::new("Hello").unwrap();
978    /// let bytes = Bytes::<16>::from_char_ptr(c_string.as_ptr());
979    ///
980    /// // From a null pointer
981    /// let null_bytes = Bytes::<16>::from_char_ptr(core::ptr::null());
982    /// // Returns zero-initialized Bytes
983    ///
984    /// // Truncation example
985    /// let long_string = CString::new("This is a very long string").unwrap();
986    /// let short_bytes = Bytes::<8>::from_char_ptr(long_string.as_ptr());
987    /// // Only first 8 bytes are copied
988    /// ```
989    pub fn from_char_ptr(ptr: *const c_char) -> Self {
990        if ptr.is_null() {
991            return Self::new();
992        }
993
994        let mut array = [0u8; SIZE];
995
996        let mut i = 0usize ;
997        for byte in unsafe { CStr::from_ptr(ptr) }.to_bytes() {
998            if i > SIZE - 1{
999                break;
1000            }
1001            array[i] = *byte;
1002            i += 1;
1003        }
1004
1005        Self( array )
1006    }
1007
1008
1009    /// Creates a new `Bytes` instance from a C unsigned char pointer.
1010    /// 
1011    /// 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`.
1012    /// 
1013    /// # Parameters
1014    /// * `ptr` - A pointer to an array of unsigned chars (`*const c_uchar`)
1015    /// 
1016    /// # Safety
1017    /// While this function is not marked unsafe, it internally uses `unsafe` code to dereference the pointer. The caller must ensure that:
1018    /// - If not null, the pointer points to a valid array of unsigned chars with at least `SIZE` bytes
1019    /// - The memory the pointer references remains valid for the duration of the call
1020    /// 
1021    /// # Returns
1022    /// A `Bytes` instance containing the data from the source pointer, or zero-initialized if the pointer is null.
1023    /// 
1024    /// # Examples
1025    /// ```
1026    /// use osal_rs::utils::Bytes;
1027    ///
1028    /// // From a C unsigned char pointer
1029    /// let data = [b'H', b'e', b'l', b'l', b'o', 0];
1030    /// let bytes = Bytes::<16>::from_uchar_ptr(data.as_ptr());
1031    /// 
1032    /// // From a null pointer
1033    /// let null_bytes = Bytes::<16>::from_uchar_ptr(core::ptr::null());  
1034    /// // Returns zero-initialized Bytes
1035    /// 
1036    /// // Truncation example
1037    /// 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];
1038    /// let short_bytes = Bytes::<8>::from_uchar_ptr(long_data.as_ptr());
1039    /// // Only first 8 bytes are copied
1040    /// ```
1041    pub fn from_uchar_ptr(ptr: *const c_uchar) -> Self {
1042        if ptr.is_null() {
1043            return Self::new();
1044        }
1045
1046        let mut array = [0u8; SIZE];
1047
1048        let mut i = 0usize ;
1049        for byte in unsafe { core::slice::from_raw_parts(ptr, SIZE) } {
1050            if i > SIZE - 1{
1051                break;
1052            }
1053            array[i] = *byte;
1054            i += 1;
1055        }
1056
1057        Self( array )
1058    }
1059
1060    /// Creates a new `Bytes` instance from any type implementing `ToString`.
1061    ///
1062    /// This is a convenience wrapper around [`from_str`](Self::from_str)
1063    /// that first converts the input to a string.
1064    ///
1065    /// # Parameters
1066    ///
1067    /// * `str` - Any value that implements `ToString`
1068    ///
1069    /// # Returns
1070    ///
1071    /// A `Bytes` instance containing the string representation of the input.
1072    ///
1073    /// # Examples
1074    ///
1075    /// ```
1076    /// use osal_rs::utils::Bytes;
1077    ///
1078    /// // From integer
1079    /// let num_bytes = Bytes::<8>::from_as_sync_str(&42);
1080    ///
1081    /// // From String
1082    /// let string = String::from("Task");
1083    /// let str_bytes = Bytes::<16>::from_as_sync_str(&string);
1084    ///
1085    /// // From custom type with ToString
1086    /// #[derive(Debug)]
1087    /// struct TaskId(u32);
1088    /// impl ToString for TaskId {
1089    ///     fn to_string(&self) -> String {
1090    ///         format!("Task-{}", self.0)
1091    ///     }
1092    /// }
1093    /// let task_bytes = Bytes::<16>::from_as_sync_str(&TaskId(5));
1094    /// ```
1095    #[inline]
1096    pub fn from_as_sync_str(str: &impl ToString) -> Self {
1097        Self::from_str(&str.to_string())
1098    }
1099
1100    /// Creates a new `Bytes` instance from a byte slice.
1101    /// 
1102    /// 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.
1103    /// 
1104    /// # Parameters
1105    /// * `bytes` - The source byte slice to convert
1106    /// 
1107    /// # Returns
1108    /// A `Bytes` instance containing the data from the byte slice.
1109    /// 
1110    /// # Examples
1111    /// ```
1112    /// use osal_rs::utils::Bytes;
1113    /// 
1114    /// let data = b"Hello";
1115    /// let bytes = Bytes::<16>::from_bytes(data);
1116    /// // Result: [b'H', b'e', b'l', b'l', b'o', 0, 0, 0, ...]
1117    /// ```
1118    pub fn from_bytes(bytes: &[u8]) -> Self {
1119        let mut array = [0u8; SIZE];
1120        let len = core::cmp::min(bytes.len(), SIZE);
1121        array[..len].copy_from_slice(&bytes[..len]);
1122        Self( array )
1123    }
1124
1125    /// Fills a mutable string slice with the contents of the byte array.
1126    ///
1127    /// Attempts to convert the internal byte array to a UTF-8 string and
1128    /// copies it into the destination string slice. Only copies up to the
1129    /// minimum of the source and destination lengths.
1130    ///
1131    /// # Parameters
1132    ///
1133    /// * `dest` - The destination string slice to fill
1134    ///
1135    /// # Returns
1136    ///
1137    /// `Ok(())` if the operation succeeds, or `Err(Error::StringConversionError)` if the byte array cannot be converted to a valid UTF-8 string.
1138    ///
1139    /// # Examples
1140    ///
1141    /// ```
1142    /// use osal_rs::utils::Bytes;
1143    ///
1144    /// let mut bytes = Bytes::<16>::from_str("Hello World");
1145    ///
1146    /// let mut output = String::from("                "); // 16 spaces
1147    /// bytes.fill_str(output.as_mut_str());
1148    ///
1149    /// assert_eq!(&output[..11], "Hello World");
1150    /// ```
1151    pub fn fill_str(&mut self, dest: &mut str) -> Result<()>{
1152        let Ok(str) = from_utf8_mut(&mut self.0) else {
1153            return Err(Error::StringConversionError);
1154        };
1155
1156        let len = core::cmp::min(str.len(), dest.len());
1157        unsafe {
1158            dest.as_bytes_mut()[..len].copy_from_slice(&str.as_bytes()[..len]);
1159        }
1160        Ok(())
1161    }
1162
1163    /// Creates a new `Bytes` instance from a C string pointer.
1164    ///
1165    /// This is a convenience wrapper around [`from_char_ptr`](Self::from_char_ptr) that directly converts a C string pointer to a `Bytes` instance.
1166    /// 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`.
1167    ///
1168    /// # Parameters
1169    ///
1170    /// * `str` - A pointer to a null-terminated C string (`*const c_char`)
1171    ///
1172    /// # Safety
1173    ///
1174    /// This method uses `unsafe` code to dereference the pointer. The caller must ensure that:
1175    /// - If not null, the pointer points to a valid null-terminated C string
1176    /// - The memory the pointer references remains valid for the duration of the call
1177    ///
1178    /// - 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.
1179    ///
1180    /// # Returns
1181    ///
1182    /// A `Bytes` instance containing the C string data, or zero-initialized if the pointer is null.
1183    ///
1184    /// # Examples
1185    ///
1186    /// ```
1187    /// use osal_rs::utils::Bytes;
1188    /// use std::ffi::CString;
1189    ///
1190    /// // From a CString
1191    /// let c_string = CString::new("Hello").unwrap();
1192    /// let bytes = Bytes::<16>::from_cstr(c_string.as_ptr());
1193    ///
1194    /// // From a null pointer
1195    /// let null_bytes = Bytes::<16>::from_cstr(core::ptr::null());
1196    /// // Returns zero-initialized Bytes
1197    ///
1198    /// // Truncation example
1199    /// let long_string = CString::new("This is a very long string").unwrap();
1200    /// let short_bytes = Bytes::<8>::from_cstr(long_string.as_ptr());
1201    /// // Only first 8 bytes are copied
1202    /// ```
1203    #[inline]
1204    pub fn from_cstr(str: *const c_char) -> Self {
1205        if str.is_null() {
1206            return Self::new();
1207        }
1208
1209        Self::from_bytes(unsafe { CStr::from_ptr(str) }.to_bytes())
1210    }
1211
1212    /// Converts the byte array to a C string reference.
1213    ///
1214    /// Creates a `CStr` reference from the internal byte array, treating it as
1215    /// a null-terminated C string. This is useful for passing strings to C FFI
1216    /// functions that expect `*const c_char` or `&CStr`.
1217    ///
1218    /// # Safety
1219    ///
1220    /// This method assumes the byte array is already null-terminated. All
1221    /// constructors (`new()`, `from_str()`, `from_char_ptr()`, etc.) guarantee
1222    /// this property by initializing with `[0u8; SIZE]`.
1223    ///
1224    /// However, if you've manually modified the array via `DerefMut`,
1225    /// you must ensure the last byte remains 0.
1226    ///
1227    /// # Returns
1228    ///
1229    /// A reference to a `CStr` with lifetime tied to `self`.
1230    ///
1231    /// # Examples
1232    ///
1233    /// ```no_run
1234    /// use osal_rs::utils::Bytes;
1235    ///
1236    /// let bytes = Bytes::<16>::from_str("Hello");
1237    /// let c_str = bytes.as_cstr();
1238    ///
1239    /// unsafe extern "C" {
1240    ///     fn print_string(s: *const core::ffi::c_char);
1241    /// }
1242    ///
1243    /// unsafe {
1244    ///     print_string(c_str.as_ptr());
1245    /// }
1246    /// ```
1247    #[inline]
1248    pub fn as_cstr(&self) -> &CStr {
1249        unsafe {
1250            CStr::from_ptr(self.0.as_ptr() as *const c_char)
1251        }
1252    }
1253
1254    /// Converts the byte array to a C string reference, ensuring null-termination.
1255    ///
1256    /// This is a safer version of `as_cstr()` that explicitly guarantees
1257    /// null-termination by modifying the last byte. Use this if you've
1258    /// manually modified the array and want to ensure it's null-terminated.
1259    ///
1260    /// # Returns
1261    ///
1262    /// A reference to a `CStr` with lifetime tied to `self`.
1263    ///
1264    /// # Examples
1265    ///
1266    /// ```
1267    /// use osal_rs::utils::Bytes;
1268    /// 
1269    /// let mut bytes = Bytes::<16>::new();
1270    /// bytes[0] = b'H';
1271    /// bytes[1] = b'i';
1272    /// // After manual modification, ensure null-termination
1273    /// let c_str = bytes.as_cstr_mut();
1274    /// ```
1275    #[inline]
1276    pub fn as_cstr_mut(&mut self) -> &CStr {
1277        unsafe {
1278            self.0[SIZE - 1] = 0; // Ensure null-termination
1279            CStr::from_ptr(self.0.as_ptr() as *const c_char)
1280        }
1281    }
1282
1283    /// Appends a string slice to the existing content in the `Bytes` buffer.
1284    ///
1285    /// This method finds the current end of the content (first null byte) and appends
1286    /// the provided string starting from that position. If the buffer is already full
1287    /// or if the appended content would exceed the buffer size, the content is truncated
1288    /// to fit within the `SIZE` limit.
1289    ///
1290    /// # Parameters
1291    ///
1292    /// * `str` - The string slice to append
1293    ///
1294    /// # Examples
1295    ///
1296    /// ```
1297    /// use osal_rs::utils::Bytes;
1298    ///
1299    /// let mut bytes = Bytes::<16>::from_str("Hello");
1300    /// bytes.append_str(" World");
1301    /// assert_eq!(bytes.as_str(), "Hello World");
1302    ///
1303    /// // Truncation when exceeding buffer size
1304    /// let mut small_bytes = Bytes::<8>::from_str("Hi");
1305    /// small_bytes.append_str(" there friend");
1306    /// assert_eq!(small_bytes.as_str(), "Hi there");
1307    /// ```
1308    pub fn append_str(&mut self, str: &str) {
1309        let current_len = self.0.iter().position(|&b| b == 0).unwrap_or(SIZE);
1310        let mut i = current_len;
1311        for byte in str.as_bytes() {
1312            if i > SIZE - 1{
1313                break;
1314            }
1315            self.0[i] = *byte;
1316            i += 1;
1317        }
1318    }
1319
1320    /// Appends content from any type implementing `AsSyncStr` to the buffer.
1321    ///
1322    /// This method accepts any type that implements the `AsSyncStr` trait, converts
1323    /// it to a string slice, and appends it to the existing content. If the buffer
1324    /// is already full or if the appended content would exceed the buffer size,
1325    /// the content is truncated to fit within the `SIZE` limit.
1326    ///
1327    /// # Parameters
1328    ///
1329    /// * `c_str` - A reference to any type implementing `AsSyncStr`
1330    ///
1331    /// # Examples
1332    ///
1333    /// ```
1334    /// use osal_rs::utils::Bytes;
1335    ///
1336    /// let mut bytes = Bytes::<16>::from_str("Hello");
1337    /// let other_bytes = Bytes::<8>::from_str(" World");
1338    /// bytes.append_as_sync_str(&other_bytes);
1339    /// assert_eq!(bytes.as_str(), "Hello World");
1340    /// ```
1341    pub fn append_as_sync_str(&mut self, c_str: & impl AsSyncStr) {
1342        let current_len = self.0.iter().position(|&b| b == 0).unwrap_or(SIZE);
1343        let mut i = current_len;
1344        for byte in c_str.as_str().as_bytes() {
1345            if i > SIZE - 1{
1346                break;
1347            }
1348            self.0[i] = *byte;
1349            i += 1;
1350        }
1351    }
1352
1353    /// Appends raw bytes to the existing content in the `Bytes` buffer.
1354    ///
1355    /// This method finds the current end of the content (first null byte) and appends
1356    /// the provided byte slice starting from that position. If the buffer is already
1357    /// full or if the appended content would exceed the buffer size, the content is
1358    /// truncated to fit within the `SIZE` limit.
1359    ///
1360    /// # Parameters
1361    ///
1362    /// * `bytes` - The byte slice to append
1363    ///
1364    /// # Examples
1365    ///
1366    /// ```
1367    /// use osal_rs::utils::Bytes;
1368    ///
1369    /// let mut bytes = Bytes::<16>::from_str("Hello");
1370    /// bytes.append_bytes(b" World");
1371    /// assert_eq!(bytes.as_str(), "Hello World");
1372    ///
1373    /// // Appending arbitrary bytes
1374    /// let mut data = Bytes::<16>::from_str("Data: ");
1375    /// data.append_bytes(&[0x41, 0x42, 0x43]);
1376    /// assert_eq!(data.as_str(), "Data: ABC");
1377    /// ```
1378    pub fn append_bytes(&mut self, bytes: &[u8]) {
1379        let current_len = self.0.iter().position(|&b| b == 0).unwrap_or(SIZE);
1380        let mut i = current_len;
1381        for byte in bytes {
1382            if i > SIZE - 1{
1383                break;
1384            }
1385            self.0[i] = *byte;
1386            i += 1;
1387        }
1388    }
1389
1390    /// Appends the content of another `Bytes` instance to this buffer.
1391    ///
1392    /// This method allows appending content from a `Bytes` instance of a different
1393    /// size (specified by the generic parameter `OHTER_SIZE`). The method finds the
1394    /// current end of the content (first null byte) and appends the content from the
1395    /// other `Bytes` instance. If the buffer is already full or if the appended content
1396    /// would exceed the buffer size, the content is truncated to fit within the `SIZE` limit.
1397    ///
1398    /// # Type Parameters
1399    ///
1400    /// * `OTHER_SIZE` - The size of the source `Bytes` buffer (can be different from `SIZE`)
1401    ///
1402    /// # Parameters
1403    ///
1404    /// * `other` - A reference to the `Bytes` instance to append
1405    ///
1406    /// # Examples
1407    ///
1408    /// ```
1409    /// use osal_rs::utils::Bytes;
1410    ///
1411    /// let mut bytes = Bytes::<16>::from_str("Hello");
1412    /// let other = Bytes::<8>::from_str(" World");
1413    /// bytes.append(&other);
1414    /// assert_eq!(bytes.as_str(), "Hello World");
1415    ///
1416    /// // Appending from a larger buffer
1417    /// let mut small = Bytes::<8>::from_str("Hi");
1418    /// let large = Bytes::<32>::from_str(" there friend");
1419    /// small.append(&large);
1420    /// assert_eq!(small.as_str(), "Hi there");
1421    /// ```
1422    pub fn append<const OTHER_SIZE: usize>(&mut self, other: &Bytes<OTHER_SIZE>) {
1423        let current_len = self.0.iter().position(|&b| b == 0).unwrap_or(SIZE);
1424        let mut i = current_len;
1425        for &byte in other.0.iter() {
1426            if i > SIZE - 1{
1427                break;
1428            }
1429            self.0[i] = byte;
1430            i += 1;
1431        }
1432    }
1433
1434
1435    /// Prepends a string slice to the existing content in the `Bytes` buffer.
1436    ///
1437    /// This method inserts the provided string at the beginning of the buffer,
1438    /// shifting the existing content to the right. If the combined length exceeds
1439    /// `SIZE`, the existing content is truncated to fit within the buffer.
1440    ///
1441    /// # Parameters
1442    ///
1443    /// * `str` - The string slice to prepend
1444    ///
1445    /// # Examples
1446    ///
1447    /// ```
1448    /// use osal_rs::utils::Bytes;
1449    ///
1450    /// let mut bytes = Bytes::<16>::from_str("World");
1451    /// bytes.prepend_str("Hello ");
1452    /// assert_eq!(bytes.as_str(), "Hello World");
1453    ///
1454    /// // Truncation when exceeding buffer size
1455    /// let mut small = Bytes::<8>::from_str("World");
1456    /// small.prepend_str("Hello ");
1457    /// assert_eq!(small.as_str(), "Hello Wo");
1458    /// ```
1459    pub fn prepend_str(&mut self, str: &str) {
1460        let current_len = self.0.iter().position(|&b| b == 0).unwrap_or(SIZE);
1461        let prefix = str.as_bytes();
1462        let prefix_len = prefix.len().min(SIZE);
1463        let keep_len = (SIZE - prefix_len).min(current_len);
1464        if keep_len > 0 {
1465            self.0.copy_within(0..keep_len, prefix_len);
1466        }
1467        self.0[..prefix_len].copy_from_slice(&prefix[..prefix_len]);
1468        let new_len = prefix_len + keep_len;
1469        if new_len < SIZE {
1470            self.0[new_len] = 0;
1471        }
1472    }
1473
1474    /// Prepends content from any type implementing `AsSyncStr` to the buffer.
1475    ///
1476    /// This method accepts any type that implements the `AsSyncStr` trait, converts
1477    /// it to a string slice, and prepends it to the existing content. If the combined
1478    /// length exceeds `SIZE`, the existing content is truncated to fit.
1479    ///
1480    /// # Parameters
1481    ///
1482    /// * `c_str` - A reference to any type implementing `AsSyncStr`
1483    ///
1484    /// # Examples
1485    ///
1486    /// ```
1487    /// use osal_rs::utils::Bytes;
1488    ///
1489    /// let mut bytes = Bytes::<16>::from_str("World");
1490    /// let prefix = Bytes::<8>::from_str("Hello ");
1491    /// bytes.prepend_as_sync_str(&prefix);
1492    /// assert_eq!(bytes.as_str(), "Hello World");
1493    /// ```
1494    pub fn prepend_as_sync_str(&mut self, c_str: & impl AsSyncStr) {
1495        self.prepend_str(c_str.as_str());
1496    }
1497
1498    /// Prepends raw bytes to the existing content in the `Bytes` buffer.
1499    ///
1500    /// This method inserts the provided byte slice at the beginning of the buffer,
1501    /// shifting the existing content to the right. If the combined length exceeds
1502    /// `SIZE`, the existing content is truncated to fit within the buffer.
1503    ///
1504    /// # Parameters
1505    ///
1506    /// * `bytes` - The byte slice to prepend
1507    ///
1508    /// # Examples
1509    ///
1510    /// ```
1511    /// use osal_rs::utils::Bytes;
1512    ///
1513    /// let mut bytes = Bytes::<16>::from_str("World");
1514    /// bytes.prepend_bytes(b"Hello ");
1515    /// assert_eq!(bytes.as_str(), "Hello World");
1516    ///
1517    /// // Prepending arbitrary bytes
1518    /// let mut data = Bytes::<16>::from_str("BC");
1519    /// data.prepend_bytes(&[0x41]); // 'A'
1520    /// assert_eq!(data.as_str(), "ABC");
1521    /// ```
1522    pub fn prepend_bytes(&mut self, bytes: &[u8]) {
1523        let current_len = self.0.iter().position(|&b| b == 0).unwrap_or(SIZE);
1524        let prefix_len = bytes.len().min(SIZE);
1525        let keep_len = (SIZE - prefix_len).min(current_len);
1526        if keep_len > 0 {
1527            self.0.copy_within(0..keep_len, prefix_len);
1528        }
1529        self.0[..prefix_len].copy_from_slice(&bytes[..prefix_len]);
1530        let new_len = prefix_len + keep_len;
1531        if new_len < SIZE {
1532            self.0[new_len] = 0;
1533        }
1534    }
1535
1536    /// Prepends the content of another `Bytes` instance to this buffer.
1537    ///
1538    /// This method allows prepending content from a `Bytes` instance of a different
1539    /// size (specified by the generic parameter `OTHER_SIZE`). The method inserts the
1540    /// content of the other `Bytes` at the beginning, shifting existing content to the
1541    /// right. If the combined length exceeds `SIZE`, the existing content is truncated.
1542    ///
1543    /// # Type Parameters
1544    ///
1545    /// * `OTHER_SIZE` - The size of the source `Bytes` buffer (can be different from `SIZE`)
1546    ///
1547    /// # Parameters
1548    ///
1549    /// * `other` - A reference to the `Bytes` instance to prepend
1550    ///
1551    /// # Examples
1552    ///
1553    /// ```
1554    /// use osal_rs::utils::Bytes;
1555    ///
1556    /// let mut bytes = Bytes::<16>::from_str("World");
1557    /// let prefix = Bytes::<8>::from_str("Hello ");
1558    /// bytes.prepend(&prefix);
1559    /// assert_eq!(bytes.as_str(), "Hello World");
1560    ///
1561    /// // Prepending from a larger buffer with truncation
1562    /// let mut small = Bytes::<8>::from_str("end");
1563    /// let large = Bytes::<32>::from_str("begin_");
1564    /// small.prepend(&large);
1565    /// assert_eq!(small.as_str(), "begin_en");
1566    /// ```
1567    pub fn prepend<const OTHER_SIZE: usize>(&mut self, other: &Bytes<OTHER_SIZE>) {
1568        let other_len = other.0.iter().position(|&b| b == 0).unwrap_or(OTHER_SIZE);
1569        self.prepend_bytes(&other.0[..other_len]);
1570    }
1571
1572    /// Clears all content from the buffer, filling it with zeros.
1573    ///
1574    /// This method resets the entire internal byte array to zeros, effectively
1575    /// clearing any stored data. After calling this method, the buffer will be
1576    /// empty and ready for new content.
1577    ///
1578    /// # Examples
1579    ///
1580    /// ```
1581    /// use osal_rs::utils::Bytes;
1582    ///
1583    /// let mut bytes = Bytes::<16>::from_str("Hello");
1584    /// assert!(!bytes.is_empty());
1585    ///
1586    /// bytes.clear();
1587    /// assert!(bytes.is_empty());
1588    /// assert_eq!(bytes.len(), 0);
1589    /// ```
1590    pub fn clear(&mut self) {
1591        for byte in self.0.iter_mut() {
1592            *byte = 0;
1593        }
1594    }
1595
1596    /// Returns the length of the content in the buffer.
1597    ///
1598    /// The length is determined by finding the position of the first null byte (0).
1599    /// If no null byte is found, returns `SIZE`, indicating the buffer is completely
1600    /// filled with non-zero data.
1601    ///
1602    /// # Returns
1603    ///
1604    /// The number of bytes before the first null terminator, or `SIZE` if the
1605    /// buffer is completely filled.
1606    ///
1607    /// # Examples
1608    ///
1609    /// ```
1610    /// use osal_rs::utils::Bytes;
1611    ///
1612    /// let bytes = Bytes::<16>::from_str("Hello");
1613    /// assert_eq!(bytes.len(), 5);
1614    ///
1615    /// let empty = Bytes::<16>::new();
1616    /// assert_eq!(empty.len(), 0);
1617    ///
1618    /// // Buffer completely filled (no null terminator)
1619    /// let mut full = Bytes::<4>::new();
1620    /// full[0] = b'A';
1621    /// full[1] = b'B';
1622    /// full[2] = b'C';
1623    /// full[3] = b'D';
1624    /// assert_eq!(full.len(), 4);
1625    /// ```
1626    #[inline]
1627    pub fn len(&self) -> usize {
1628        self.0.iter().position(|&b| b == 0).unwrap_or(SIZE)
1629    }
1630
1631    /// Returns a byte slice of the content in the buffer.
1632    /// 
1633    /// 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.
1634    /// 
1635    /// # Returns
1636    /// A byte slice containing the content of the buffer up to the first null terminator.
1637    /// 
1638    /// # Examples
1639    /// ```
1640    /// use osal_rs::utils::Bytes;
1641    /// 
1642    /// let bytes = Bytes::<16>::from_str("Hello");
1643    /// assert_eq!(bytes.as_raw_bytes(), b"Hello");
1644    /// 
1645    /// let empty = Bytes::<16>::new();
1646    /// assert_eq!(empty.as_raw_bytes(), b"");
1647    /// 
1648    /// let full = Bytes::<4>::from_str("ABCD");
1649    /// assert_eq!(full.as_raw_bytes(), b"ABCD");
1650    /// ``` 
1651    #[inline]
1652    pub fn as_raw_bytes(&self) -> &[u8] {
1653        &self.0[..self.len()]
1654    }
1655
1656    /// Returns the fixed size of the buffer.
1657    /// 
1658    /// 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.
1659    /// # Returns
1660    /// The fixed size of the buffer in bytes (`SIZE`).
1661    /// # Examples
1662    /// ```
1663    /// use osal_rs::utils::Bytes;
1664    /// 
1665    /// let bytes = Bytes::<32>::new();
1666    /// assert_eq!(bytes.size(), 32);
1667    /// 
1668    /// let other = Bytes::<128>::from_str("Hello");
1669    /// assert_eq!(other.size(), 128);
1670    /// ```
1671    #[inline]
1672    pub const fn size(&self) -> usize {
1673        SIZE
1674    }
1675
1676    /// Checks if the buffer is empty.
1677    ///
1678    /// A buffer is considered empty if all bytes are zero. This method searches
1679    /// for the first non-zero byte to determine emptiness.
1680    ///
1681    /// # Returns
1682    ///
1683    /// `true` if all bytes are zero, `false` otherwise.
1684    ///
1685    /// # Examples
1686    ///
1687    /// ```
1688    /// use osal_rs::utils::Bytes;
1689    ///
1690    /// let empty = Bytes::<16>::new();
1691    /// assert!(empty.is_empty());
1692    ///
1693    /// let bytes = Bytes::<16>::from_str("Hello");
1694    /// assert!(!bytes.is_empty());
1695    ///
1696    /// let mut cleared = Bytes::<16>::from_str("Test");
1697    /// cleared.clear();
1698    /// assert!(cleared.is_empty());
1699    /// ```
1700    #[inline]
1701    pub fn is_empty(&self) -> bool {
1702        self.0.iter().position(|&b| b != 0).is_none()
1703    }
1704
1705    /// Returns the total capacity of the buffer.
1706    ///
1707    /// This is the fixed size of the internal byte array, determined at compile
1708    /// time by the generic `SIZE` parameter. The capacity never changes during
1709    /// the lifetime of the `Bytes` instance.
1710    ///
1711    /// # Returns
1712    ///
1713    /// The total capacity in bytes (`SIZE`).
1714    ///
1715    /// # Examples
1716    ///
1717    /// ```
1718    /// use osal_rs::utils::Bytes;
1719    ///
1720    /// let bytes = Bytes::<32>::new();
1721    /// assert_eq!(bytes.capacity(), 32);
1722    ///
1723    /// let other = Bytes::<128>::from_str("Hello");
1724    /// assert_eq!(other.capacity(), 128);
1725    /// ```
1726    #[inline]
1727    pub fn capacity(&self) -> usize {
1728        SIZE
1729    }
1730
1731    /// Replaces all occurrences of a byte pattern with another pattern.
1732    ///
1733    /// This method searches for all occurrences of the `find` byte sequence within
1734    /// the buffer and replaces them with the `replace` byte sequence. The replacement
1735    /// is performed in a single pass, and the method handles cases where the replacement
1736    /// is larger, smaller, or equal in size to the pattern being searched for.
1737    ///
1738    /// # Parameters
1739    ///
1740    /// * `find` - The byte pattern to search for
1741    /// * `replace` - The byte pattern to replace with
1742    ///
1743    /// # Returns
1744    ///
1745    /// * `Ok(())` - If all replacements were successful
1746    /// * `Err(Error::StringConversionError)` - If the replacement would exceed the buffer capacity
1747    ///
1748    /// # Behavior
1749    ///
1750    /// - Empty `find` patterns are ignored (returns `Ok(())` immediately)
1751    /// - Multiple occurrences are replaced in a single pass
1752    /// - Content is properly shifted when replacement size differs from find size
1753    /// - Null terminators and trailing bytes are correctly maintained
1754    /// - Overlapping patterns are not re-matched (avoids infinite loops)
1755    ///
1756    /// # Examples
1757    ///
1758    /// ```
1759    /// use osal_rs::utils::Bytes;
1760    ///
1761    /// // Same length replacement
1762    /// let mut bytes = Bytes::<16>::from_str("Hello World");
1763    /// bytes.replace(b"World", b"Rust!").unwrap();
1764    /// assert_eq!(bytes.as_str(), "Hello Rust!");
1765    ///
1766    /// // Shorter replacement
1767    /// let mut bytes2 = Bytes::<16>::from_str("aabbcc");
1768    /// bytes2.replace(b"bb", b"X").unwrap();
1769    /// assert_eq!(bytes2.as_str(), "aaXcc");
1770    ///
1771    /// // Longer replacement
1772    /// let mut bytes3 = Bytes::<16>::from_str("Hi");
1773    /// bytes3.replace(b"Hi", b"Hello").unwrap();
1774    /// assert_eq!(bytes3.as_str(), "Hello");
1775    ///
1776    /// // Multiple occurrences
1777    /// let mut bytes4 = Bytes::<32>::from_str("foo bar foo");
1778    /// bytes4.replace(b"foo", b"baz").unwrap();
1779    /// assert_eq!(bytes4.as_str(), "baz bar baz");
1780    ///
1781    /// // Buffer overflow error
1782    /// let mut small = Bytes::<8>::from_str("Hello");
1783    /// assert!(small.replace(b"Hello", b"Hello World").is_err());
1784    /// ```
1785    pub fn replace(&mut self, find: &[u8], replace: &[u8]) -> Result<()> {
1786        if find.is_empty() {
1787            return Ok(());
1788        }
1789        
1790        let mut i = 0;
1791        loop {
1792            let current_len = self.len();
1793            
1794            // Exit if we've reached the end
1795            if i >= current_len {
1796                break;
1797            }
1798            
1799            // Check if pattern starts at position i
1800            if i + find.len() <= current_len && self.0[i..i + find.len()] == *find {
1801                let remaining_len = current_len - (i + find.len());
1802                let new_len = i + replace.len() + remaining_len;
1803                
1804                // Check if replacement fits in buffer
1805                if new_len > SIZE {
1806                    return Err(Error::StringConversionError);
1807                }
1808                
1809                // Shift remaining content if sizes differ
1810                if replace.len() != find.len() {
1811                    self.0.copy_within(
1812                        i + find.len()..i + find.len() + remaining_len,
1813                        i + replace.len()
1814                    );
1815                }
1816                
1817                // Insert replacement bytes
1818                self.0[i..i + replace.len()].copy_from_slice(replace);
1819                
1820                // Update null terminator position
1821                if new_len < SIZE {
1822                    self.0[new_len] = 0;
1823                }
1824                
1825                // Clear trailing bytes if content shrunk
1826                if new_len < current_len {
1827                    for j in (new_len + 1)..=current_len {
1828                        if j < SIZE {
1829                            self.0[j] = 0;
1830                        }
1831                    }
1832                }
1833                
1834                // Move past the replacement to avoid infinite loops
1835                i += replace.len();
1836            } else {
1837                i += 1;
1838            }
1839        }
1840        
1841        Ok(())
1842    }
1843
1844    /// Converts the `Bytes` instance to a byte slice.
1845    ///
1846    /// This method provides a convenient way to access the internal byte array
1847    /// as a slice, which can be useful for C FFI or other operations that
1848    /// require byte slices.
1849    ///
1850    /// # Examples
1851    ///
1852    /// ```
1853    /// use osal_rs::utils::Bytes;
1854    ///
1855    /// let bytes = Bytes::<8>::from_str("example");
1856    /// let byte_slice = bytes.to_bytes();
1857    /// assert_eq!(byte_slice, b"example\0");
1858    /// ```
1859    #[inline]
1860    pub fn to_bytes(&self) -> &[u8] {
1861        &self.0
1862    }
1863
1864    /// Pops the last byte from the buffer and returns it.
1865    ///
1866    /// This method removes the last byte of content (before the first null terminator)
1867    /// 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.
1868    ///
1869    /// # Returns
1870    ///
1871    /// * `Some(u8)` - The last byte of content if the buffer is not empty
1872    /// * `None` - If the buffer is empty
1873    ///
1874    /// # Examples
1875    /// ```
1876    /// use osal_rs::utils::Bytes;
1877    ///
1878    /// let mut bytes = Bytes::<16>::from_str("Hello");
1879    /// assert_eq!(bytes.pop(), Some(b'o'));
1880    /// assert_eq!(bytes.as_str(), "Hell");
1881    /// 
1882    /// // Pop until empty
1883    /// assert_eq!(bytes.pop(), Some(b'l'));
1884    /// assert_eq!(bytes.pop(), Some(b'l'));
1885    /// assert_eq!(bytes.pop(), Some(b'e'));
1886    /// assert_eq!(bytes.pop(), Some(b'H'));
1887    /// assert_eq!(bytes.pop(), None);
1888    /// ``` 
1889    pub fn pop(&mut self) -> Option<u8> {
1890        let len = self.len();
1891        if len == 0 {
1892            None
1893        } else {
1894            let byte = self.0[len - 1];
1895            self.0[len - 1] = 0; // Clear the popped byte
1896            Some(byte)
1897        }
1898    }
1899
1900    /// Pushes a byte to the end of the content in the buffer.
1901    ///
1902    /// # Parameters
1903    ///
1904    /// * `byte` - The byte to push into the buffer
1905    ///
1906    /// # Returns
1907    ///
1908    /// * `Ok(())` - If the byte was successfully pushed
1909    /// * `Err(Error::StringConversionError)` - If the buffer is full
1910    ///
1911    /// # Examples
1912    ///
1913    /// ```
1914    /// use osal_rs::utils::Bytes;
1915    ///
1916    /// let mut bytes = Bytes::<16>::from_str("Hello");
1917    /// assert_eq!(bytes.push(b'!'), Ok(()));
1918    /// assert_eq!(bytes.as_str(), "Hello!");
1919    /// ```
1920    pub fn push(&mut self, byte: u8) -> Result<()> {
1921        let len = self.len();
1922        if len >= SIZE {
1923            Err(Error::StringConversionError) // Buffer is full
1924        } else {
1925            self.0[len] = byte;
1926            Ok(())
1927        }
1928    }
1929
1930    /// Pops the last byte from the buffer and returns it as a character.
1931    ///
1932    /// This method removes the last byte of content (before the first null terminator)
1933    /// 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.
1934    ///
1935    /// # Returns
1936    ///
1937    /// * `Some(char)` - The last byte of content as a character if the buffer is not empty and the byte is a valid character
1938    /// * `None` - If the buffer is empty or if the byte cannot be converted to a valid character
1939    ///
1940    /// # Examples
1941    /// ```
1942    /// use osal_rs::utils::Bytes;
1943    ///
1944    /// let mut bytes = Bytes::<16>::from_str("Hello");
1945    /// assert_eq!(bytes.pop_char(), Some('o'));
1946    /// assert_eq!(bytes.as_str(), "Hell");
1947    /// 
1948    /// // Pop until empty
1949    /// assert_eq!(bytes.pop_char(), Some('l'));
1950    /// assert_eq!(bytes.pop_char(), Some('l'));
1951    /// assert_eq!(bytes.pop_char(), Some('e'));
1952    /// assert_eq!(bytes.pop_char(), Some('H'));
1953    /// assert_eq!(bytes.pop_char(), None);
1954    /// ```
1955    #[inline]
1956    pub fn pop_char(&mut self) -> Option<char> {
1957        self.pop().and_then(|byte| char::from_u32(byte as u32))
1958    }
1959
1960    /// Pushes a character to the end of the content in the buffer.
1961    ///
1962    /// 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.
1963    ///
1964    /// # Parameters
1965    ///
1966    /// * `ch` - The character to push into the buffer
1967    ///
1968    /// # Returns
1969    ///
1970    /// * `Ok(())` - If the character was successfully pushed
1971    /// * `Err(Error::StringConversionError)` - If the character is not a valid ASCII character or if the buffer is full
1972    ///
1973    /// # Examples
1974    /// ```
1975    /// use osal_rs::utils::Bytes;
1976    ///
1977    /// let mut bytes = Bytes::<16>::from_str("Hello");
1978    /// assert_eq!(bytes.push_char('!'), Ok(()));
1979    /// assert_eq!(bytes.as_str(), "Hello!");
1980    /// 
1981    /// // Attempt to push a non-ASCII character
1982    /// assert!(bytes.push_char('é').is_err());
1983    /// ```
1984    pub fn push_char(&mut self, ch: char) -> Result<()> {
1985        if ch.is_ascii() {
1986            self.push(ch as u8)
1987        } else {
1988            Err(Error::StringConversionError) // Non-ASCII characters not supported
1989        }
1990    }
1991
1992    /// Checks if the content of the buffer can be interpreted as a valid UTF-8 string.
1993    ///
1994    /// 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`.
1995    ///
1996    /// # Returns
1997    ///
1998    /// * `true` - If the content can be interpreted as a valid UTF-8 string
1999    /// * `false` - If the content contains invalid UTF-8 sequences
2000    ///
2001    /// # Examples
2002    /// ```
2003    /// use osal_rs::utils::Bytes;
2004    ///
2005    /// let valid_bytes = Bytes::<16>::from_str("Hello");
2006    /// assert!(valid_bytes.is_string());
2007    /// 
2008    /// let mut invalid_bytes = Bytes::<16>::new();
2009    /// invalid_bytes[0] = 0xFF; // Invalid UTF-8 byte
2010    /// assert!(!invalid_bytes.is_string());
2011    /// ```
2012    #[inline]
2013    pub fn is_string(&self) -> bool {
2014        String::from_utf8(self.0.to_vec()).is_ok()
2015    }
2016
2017    /// Returns the buffer content as a UTF-8 string slice.
2018    ///
2019    /// Interprets the byte array as a null-terminated C string and returns
2020    /// a `&str`. If the bytes contain invalid UTF-8, returns `"Conversion error"`.
2021    ///
2022    /// This is an inherent method (no trait import required at the call site).
2023    #[inline]
2024    pub fn as_str(&self) -> &str {
2025        from_utf8(self.as_raw_bytes()).unwrap_or("Bytes::as_str() Conversion error - invalid UTF-8")
2026    }
2027
2028    /// Overwrites the buffer with a formatted string, behaving like `alloc::format!`.
2029    ///
2030    /// Clears the current content and fills the buffer with the result of formatting
2031    /// `args`. Content that exceeds `SIZE` is silently truncated.
2032    ///
2033    /// # Parameters
2034    ///
2035    /// * `args` - A [`core::fmt::Arguments`] value, typically created with [`format_args!`]
2036    ///
2037    /// # Examples
2038    ///
2039    /// ```
2040    /// use osal_rs::utils::Bytes;
2041    ///
2042    /// let mut b = Bytes::<32>::new();
2043    /// b.format(format_args!("Hello {}", 42));
2044    /// assert_eq!(b.as_str(), "Hello 42");
2045    ///
2046    /// let mut b2 = Bytes::<8>::new();
2047    /// b2.format(format_args!("{:.2}", 3.14159));
2048    /// assert_eq!(b2.as_str(), "3.14");
2049    /// ```
2050    #[inline]
2051    pub fn format(&mut self, args: Arguments<'_>) {
2052        self.clear();
2053        let _ = write(self, args);
2054    }
2055        
2056}
2057
2058/// Converts a byte slice to a hexadecimal string representation.
2059///
2060/// Each byte is converted to its two-character hexadecimal representation
2061/// in lowercase. This function allocates a new `String` on the heap.
2062///
2063/// # Parameters
2064///
2065/// * `bytes` - The byte slice to convert
2066///
2067/// # Returns
2068///
2069/// A `String` containing the hexadecimal representation of the bytes.
2070/// Each byte is represented by exactly 2 hex characters (lowercase).
2071///
2072/// # Memory Allocation
2073///
2074/// This function allocates heap memory. In memory-constrained environments,
2075/// consider using [`bytes_to_hex_into_slice`] instead.
2076///
2077/// # Examples
2078///
2079/// ```
2080/// use osal_rs::utils::bytes_to_hex;
2081/// 
2082/// let data = &[0x01, 0x23, 0xAB, 0xFF];
2083/// let hex = bytes_to_hex(data);
2084/// assert_eq!(hex, "0123abff");
2085/// 
2086/// let empty = bytes_to_hex(&[]);
2087/// assert_eq!(empty, "");
2088/// ```
2089#[inline]
2090pub fn bytes_to_hex(bytes: &[u8]) -> String {
2091    bytes.iter()
2092         .map(|b| format!("{:02x}", b))
2093         .collect()
2094}
2095
2096/// Converts a byte slice to hexadecimal representation into a pre-allocated buffer.
2097///
2098/// This is a zero-allocation version of [`bytes_to_hex`] that writes the
2099/// hexadecimal representation directly into a provided output buffer.
2100/// Suitable for embedded systems and real-time applications.
2101///
2102/// # Parameters
2103///
2104/// * `bytes` - The source byte slice to convert
2105/// * `output` - The destination buffer to write hex characters into
2106///
2107/// # Returns
2108///
2109/// The number of bytes written to the output buffer (always `bytes.len() * 2`).
2110///
2111/// # Panics
2112///
2113/// Panics if `output.len() < bytes.len() * 2`. The output buffer must be
2114/// at least twice the size of the input to hold the hex representation.
2115///
2116/// # Examples
2117///
2118/// ```
2119/// use osal_rs::utils::bytes_to_hex_into_slice;
2120/// 
2121/// let data = &[0x01, 0xAB, 0xFF];
2122/// let mut buffer = [0u8; 6];
2123/// 
2124/// let written = bytes_to_hex_into_slice(data, &mut buffer);
2125/// assert_eq!(written, 6);
2126/// assert_eq!(&buffer, b"01abff");
2127/// 
2128/// // Will panic - buffer too small
2129/// // let mut small = [0u8; 4];
2130/// // bytes_to_hex_into_slice(data, &mut small);
2131/// ```
2132pub fn bytes_to_hex_into_slice(bytes: &[u8], output: &mut [u8]) -> usize {
2133    assert!(output.len() >= bytes.len() * 2, "Buffer too small for hex conversion");
2134    let mut i = 0;
2135    for &b in bytes {
2136        let hex = format!("{:02x}", b);
2137        output[i..i+2].copy_from_slice(hex.as_bytes());
2138        i += 2;
2139    }
2140    i 
2141}
2142
2143/// Converts a hexadecimal string to a vector of bytes.
2144///
2145/// Parses a string of hexadecimal digits (case-insensitive) and converts
2146/// them to their binary representation. Each pair of hex digits becomes
2147/// one byte in the output.
2148///
2149/// # Parameters
2150///
2151/// * `hex` - A string slice containing hexadecimal digits (0-9, a-f, A-F)
2152///
2153/// # Returns
2154///
2155/// * `Ok(Vec<u8>)` - A vector containing the decoded bytes
2156/// * `Err(Error::StringConversionError)` - If the string has odd length or contains invalid hex digits
2157///
2158/// # Memory Allocation
2159///
2160/// This function allocates a `Vec` on the heap. For no-alloc environments,
2161/// use [`hex_to_bytes_into_slice`] instead.
2162///
2163/// # Examples
2164///
2165/// ```
2166/// use osal_rs::utils::hex_to_bytes;
2167/// 
2168/// // Lowercase hex
2169/// let bytes = hex_to_bytes("0123abff").unwrap();
2170/// assert_eq!(bytes, vec![0x01, 0x23, 0xAB, 0xFF]);
2171/// 
2172/// // Uppercase hex
2173/// let bytes2 = hex_to_bytes("ABCD").unwrap();
2174/// assert_eq!(bytes2, vec![0xAB, 0xCD]);
2175/// 
2176/// // Odd length - error
2177/// assert!(hex_to_bytes("ABC").is_err());
2178/// 
2179/// // Invalid character - error
2180/// assert!(hex_to_bytes("0G").is_err());
2181/// ```
2182pub fn hex_to_bytes(hex: &str) -> Result<Vec<u8>> {
2183    if hex.len() % 2 != 0 {
2184        return Err(Error::StringConversionError);
2185    }
2186
2187    let bytes_result: Result<Vec<u8>> = (0..hex.len())
2188        .step_by(2)
2189        .map(|i| {
2190            u8::from_str_radix(&hex[i..i + 2], 16)
2191                .map_err(|_| Error::StringConversionError)
2192        })
2193        .collect();
2194
2195    bytes_result
2196}
2197
2198/// Converts a hexadecimal string to bytes into a pre-allocated buffer.
2199///
2200/// This is a zero-allocation version of [`hex_to_bytes`] that writes decoded
2201/// bytes directly into a provided output buffer. Suitable for embedded systems
2202/// and real-time applications where heap allocation is not desired.
2203///
2204/// # Parameters
2205///
2206/// * `hex` - A string slice containing hexadecimal digits (0-9, a-f, A-F)
2207/// * `output` - The destination buffer to write decoded bytes into
2208///
2209/// # Returns
2210///
2211/// * `Ok(usize)` - The number of bytes written to the output buffer (`hex.len() / 2`)
2212/// * `Err(Error::StringConversionError)` - If:
2213///   - The hex string has odd length
2214///   - The output buffer is too small (`output.len() < hex.len() / 2`)
2215///   - The hex string contains invalid characters
2216///
2217/// # Examples
2218///
2219/// ```
2220/// use osal_rs::utils::hex_to_bytes_into_slice;
2221/// 
2222/// let mut buffer = [0u8; 4];
2223/// let written = hex_to_bytes_into_slice("0123abff", &mut buffer).unwrap();
2224/// assert_eq!(written, 4);
2225/// assert_eq!(buffer, [0x01, 0x23, 0xAB, 0xFF]);
2226/// 
2227/// // Buffer too small
2228/// let mut small = [0u8; 2];
2229/// assert!(hex_to_bytes_into_slice("0123abff", &mut small).is_err());
2230/// 
2231/// // Odd length string
2232/// assert!(hex_to_bytes_into_slice("ABC", &mut buffer).is_err());
2233/// ```
2234pub fn hex_to_bytes_into_slice(hex: &str, output: &mut [u8]) -> Result<usize> {
2235    if hex.len() % 2 != 0 || output.len() < hex.len() / 2 {
2236        return Err(Error::StringConversionError);
2237    }
2238
2239    for i in 0..(hex.len() / 2) {
2240        output[i] = u8::from_str_radix(&hex[2 * i..2 * i + 2], 16)
2241            .map_err(|_| Error::StringConversionError)?;
2242    }
2243
2244    Ok(hex.len() / 2)
2245}