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
1418 /// Prepends a string slice to the existing content in the `Bytes` buffer.
1419 ///
1420 /// This method inserts the provided string at the beginning of the buffer,
1421 /// shifting the existing content to the right. If the combined length exceeds
1422 /// `SIZE`, the existing content is truncated to fit within the buffer.
1423 ///
1424 /// # Parameters
1425 ///
1426 /// * `str` - The string slice to prepend
1427 ///
1428 /// # Examples
1429 ///
1430 /// ```
1431 /// use osal_rs::utils::Bytes;
1432 ///
1433 /// let mut bytes = Bytes::<16>::new_by_str("World");
1434 /// bytes.prepend_str("Hello ");
1435 /// assert_eq!(bytes.as_str(), "Hello World");
1436 ///
1437 /// // Truncation when exceeding buffer size
1438 /// let mut small = Bytes::<8>::new_by_str("World");
1439 /// small.prepend_str("Hello ");
1440 /// assert_eq!(small.as_str(), "Hello Wo");
1441 /// ```
1442 pub fn prepend_str(&mut self, str: &str) {
1443 let current_len = self.0.iter().position(|&b| b == 0).unwrap_or(SIZE);
1444 let prefix = str.as_bytes();
1445 let prefix_len = prefix.len().min(SIZE);
1446 let keep_len = (SIZE - prefix_len).min(current_len);
1447 if keep_len > 0 {
1448 self.0.copy_within(0..keep_len, prefix_len);
1449 }
1450 self.0[..prefix_len].copy_from_slice(&prefix[..prefix_len]);
1451 let new_len = prefix_len + keep_len;
1452 if new_len < SIZE {
1453 self.0[new_len] = 0;
1454 }
1455 }
1456
1457 /// Prepends content from any type implementing `AsSyncStr` to the buffer.
1458 ///
1459 /// This method accepts any type that implements the `AsSyncStr` trait, converts
1460 /// it to a string slice, and prepends it to the existing content. If the combined
1461 /// length exceeds `SIZE`, the existing content is truncated to fit.
1462 ///
1463 /// # Parameters
1464 ///
1465 /// * `c_str` - A reference to any type implementing `AsSyncStr`
1466 ///
1467 /// # Examples
1468 ///
1469 /// ```ignore
1470 /// use osal_rs::utils::Bytes;
1471 ///
1472 /// let mut bytes = Bytes::<16>::new_by_str("World");
1473 /// let prefix = Bytes::<8>::new_by_str("Hello ");
1474 /// bytes.prepend_as_sync_str(&prefix);
1475 /// assert_eq!(bytes.as_str(), "Hello World");
1476 /// ```
1477 pub fn prepend_as_sync_str(&mut self, c_str: & impl AsSyncStr) {
1478 self.prepend_str(c_str.as_str());
1479 }
1480
1481 /// Prepends raw bytes to the existing content in the `Bytes` buffer.
1482 ///
1483 /// This method inserts the provided byte slice at the beginning of the buffer,
1484 /// shifting the existing content to the right. If the combined length exceeds
1485 /// `SIZE`, the existing content is truncated to fit within the buffer.
1486 ///
1487 /// # Parameters
1488 ///
1489 /// * `bytes` - The byte slice to prepend
1490 ///
1491 /// # Examples
1492 ///
1493 /// ```
1494 /// use osal_rs::utils::Bytes;
1495 ///
1496 /// let mut bytes = Bytes::<16>::new_by_str("World");
1497 /// bytes.prepend_bytes(b"Hello ");
1498 /// assert_eq!(bytes.as_str(), "Hello World");
1499 ///
1500 /// // Prepending arbitrary bytes
1501 /// let mut data = Bytes::<16>::new_by_str("BC");
1502 /// data.prepend_bytes(&[0x41]); // 'A'
1503 /// assert_eq!(data.as_str(), "ABC");
1504 /// ```
1505 pub fn prepend_bytes(&mut self, bytes: &[u8]) {
1506 let current_len = self.0.iter().position(|&b| b == 0).unwrap_or(SIZE);
1507 let prefix_len = bytes.len().min(SIZE);
1508 let keep_len = (SIZE - prefix_len).min(current_len);
1509 if keep_len > 0 {
1510 self.0.copy_within(0..keep_len, prefix_len);
1511 }
1512 self.0[..prefix_len].copy_from_slice(&bytes[..prefix_len]);
1513 let new_len = prefix_len + keep_len;
1514 if new_len < SIZE {
1515 self.0[new_len] = 0;
1516 }
1517 }
1518
1519 /// Prepends the content of another `Bytes` instance to this buffer.
1520 ///
1521 /// This method allows prepending content from a `Bytes` instance of a different
1522 /// size (specified by the generic parameter `OTHER_SIZE`). The method inserts the
1523 /// content of the other `Bytes` at the beginning, shifting existing content to the
1524 /// right. If the combined length exceeds `SIZE`, the existing content is truncated.
1525 ///
1526 /// # Type Parameters
1527 ///
1528 /// * `OTHER_SIZE` - The size of the source `Bytes` buffer (can be different from `SIZE`)
1529 ///
1530 /// # Parameters
1531 ///
1532 /// * `other` - A reference to the `Bytes` instance to prepend
1533 ///
1534 /// # Examples
1535 ///
1536 /// ```
1537 /// use osal_rs::utils::Bytes;
1538 ///
1539 /// let mut bytes = Bytes::<16>::new_by_str("World");
1540 /// let prefix = Bytes::<8>::new_by_str("Hello ");
1541 /// bytes.prepend(&prefix);
1542 /// assert_eq!(bytes.as_str(), "Hello World");
1543 ///
1544 /// // Prepending from a larger buffer with truncation
1545 /// let mut small = Bytes::<8>::new_by_str("end");
1546 /// let large = Bytes::<32>::new_by_str("begin_");
1547 /// small.prepend(&large);
1548 /// assert_eq!(small.as_str(), "begin_en");
1549 /// ```
1550 pub fn prepend<const OTHER_SIZE: usize>(&mut self, other: &Bytes<OTHER_SIZE>) {
1551 let other_len = other.0.iter().position(|&b| b == 0).unwrap_or(OTHER_SIZE);
1552 self.prepend_bytes(&other.0[..other_len]);
1553 }
1554
1555 /// Clears all content from the buffer, filling it with zeros.
1556 ///
1557 /// This method resets the entire internal byte array to zeros, effectively
1558 /// clearing any stored data. After calling this method, the buffer will be
1559 /// empty and ready for new content.
1560 ///
1561 /// # Examples
1562 ///
1563 /// ```ignore
1564 /// use osal_rs::utils::Bytes;
1565 ///
1566 /// let mut bytes = Bytes::<16>::new_by_str("Hello");
1567 /// assert!(!bytes.is_empty());
1568 ///
1569 /// bytes.clear();
1570 /// assert!(bytes.is_empty());
1571 /// assert_eq!(bytes.len(), 0);
1572 /// ```
1573 pub fn clear(&mut self) {
1574 for byte in self.0.iter_mut() {
1575 *byte = 0;
1576 }
1577 }
1578
1579 /// Returns the length of the content in the buffer.
1580 ///
1581 /// The length is determined by finding the position of the first null byte (0).
1582 /// If no null byte is found, returns `SIZE`, indicating the buffer is completely
1583 /// filled with non-zero data.
1584 ///
1585 /// # Returns
1586 ///
1587 /// The number of bytes before the first null terminator, or `SIZE` if the
1588 /// buffer is completely filled.
1589 ///
1590 /// # Examples
1591 ///
1592 /// ```ignore
1593 /// use osal_rs::utils::Bytes;
1594 ///
1595 /// let bytes = Bytes::<16>::new_by_str("Hello");
1596 /// assert_eq!(bytes.len(), 5);
1597 ///
1598 /// let empty = Bytes::<16>::new();
1599 /// assert_eq!(empty.len(), 0);
1600 ///
1601 /// // Buffer completely filled (no null terminator)
1602 /// let mut full = Bytes::<4>::new();
1603 /// full[0] = b'A';
1604 /// full[1] = b'B';
1605 /// full[2] = b'C';
1606 /// full[3] = b'D';
1607 /// assert_eq!(full.len(), 4);
1608 /// ```
1609 #[inline]
1610 pub fn len(&self) -> usize {
1611 self.0.iter().position(|&b| b == 0).unwrap_or(SIZE)
1612 }
1613
1614 /// Returns a byte slice of the content in the buffer.
1615 ///
1616 /// 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.
1617 ///
1618 /// # Returns
1619 /// A byte slice containing the content of the buffer up to the first null terminator.
1620 ///
1621 /// # Examples
1622 /// ```ignore
1623 /// use osal_rs::utils::Bytes;
1624 ///
1625 /// let bytes = Bytes::<16>::new_by_str("Hello");
1626 /// assert_eq!(bytes.as_raw_bytes(), b"Hello");
1627 ///
1628 /// let empty = Bytes::<16>::new();
1629 /// assert_eq!(empty.as_raw_bytes(), b"");
1630 ///
1631 /// let full = Bytes::<4>::new_by_str("ABCD");
1632 /// assert_eq!(full.as_raw_bytes(), b"ABCD");
1633 /// ```
1634 #[inline]
1635 pub fn as_raw_bytes(&self) -> &[u8] {
1636 &self.0[..self.len()]
1637 }
1638
1639 /// Returns the fixed size of the buffer.
1640 ///
1641 /// 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.
1642 /// # Returns
1643 /// The fixed size of the buffer in bytes (`SIZE`).
1644 /// # Examples
1645 /// ```ignore
1646 /// use osal_rs::utils::Bytes;
1647 ///
1648 /// let bytes = Bytes::<32>::new();
1649 /// assert_eq!(bytes.size(), 32);
1650 ///
1651 /// let other = Bytes::<128>::new_by_str("Hello");
1652 /// assert_eq!(other.size(), 128);
1653 /// ```
1654 #[inline]
1655 pub const fn size(&self) -> usize {
1656 SIZE
1657 }
1658
1659 /// Checks if the buffer is empty.
1660 ///
1661 /// A buffer is considered empty if all bytes are zero. This method searches
1662 /// for the first non-zero byte to determine emptiness.
1663 ///
1664 /// # Returns
1665 ///
1666 /// `true` if all bytes are zero, `false` otherwise.
1667 ///
1668 /// # Examples
1669 ///
1670 /// ```ignore
1671 /// use osal_rs::utils::Bytes;
1672 ///
1673 /// let empty = Bytes::<16>::new();
1674 /// assert!(empty.is_empty());
1675 ///
1676 /// let bytes = Bytes::<16>::new_by_str("Hello");
1677 /// assert!(!bytes.is_empty());
1678 ///
1679 /// let mut cleared = Bytes::<16>::new_by_str("Test");
1680 /// cleared.clear();
1681 /// assert!(cleared.is_empty());
1682 /// ```
1683 #[inline]
1684 pub fn is_empty(&self) -> bool {
1685 self.0.iter().position(|&b| b != 0).is_none()
1686 }
1687
1688 /// Returns the total capacity of the buffer.
1689 ///
1690 /// This is the fixed size of the internal byte array, determined at compile
1691 /// time by the generic `SIZE` parameter. The capacity never changes during
1692 /// the lifetime of the `Bytes` instance.
1693 ///
1694 /// # Returns
1695 ///
1696 /// The total capacity in bytes (`SIZE`).
1697 ///
1698 /// # Examples
1699 ///
1700 /// ```ignore
1701 /// use osal_rs::utils::Bytes;
1702 ///
1703 /// let bytes = Bytes::<32>::new();
1704 /// assert_eq!(bytes.capacity(), 32);
1705 ///
1706 /// let other = Bytes::<128>::new_by_str("Hello");
1707 /// assert_eq!(other.capacity(), 128);
1708 /// ```
1709 #[inline]
1710 pub fn capacity(&self) -> usize {
1711 SIZE
1712 }
1713
1714 /// Replaces all occurrences of a byte pattern with another pattern.
1715 ///
1716 /// This method searches for all occurrences of the `find` byte sequence within
1717 /// the buffer and replaces them with the `replace` byte sequence. The replacement
1718 /// is performed in a single pass, and the method handles cases where the replacement
1719 /// is larger, smaller, or equal in size to the pattern being searched for.
1720 ///
1721 /// # Parameters
1722 ///
1723 /// * `find` - The byte pattern to search for
1724 /// * `replace` - The byte pattern to replace with
1725 ///
1726 /// # Returns
1727 ///
1728 /// * `Ok(())` - If all replacements were successful
1729 /// * `Err(Error::StringConversionError)` - If the replacement would exceed the buffer capacity
1730 ///
1731 /// # Behavior
1732 ///
1733 /// - Empty `find` patterns are ignored (returns `Ok(())` immediately)
1734 /// - Multiple occurrences are replaced in a single pass
1735 /// - Content is properly shifted when replacement size differs from find size
1736 /// - Null terminators and trailing bytes are correctly maintained
1737 /// - Overlapping patterns are not re-matched (avoids infinite loops)
1738 ///
1739 /// # Examples
1740 ///
1741 /// ```ignore
1742 /// use osal_rs::utils::Bytes;
1743 ///
1744 /// // Same length replacement
1745 /// let mut bytes = Bytes::<16>::new_by_str("Hello World");
1746 /// bytes.replace(b"World", b"Rust!").unwrap();
1747 /// assert_eq!(bytes.as_str(), "Hello Rust!");
1748 ///
1749 /// // Shorter replacement
1750 /// let mut bytes2 = Bytes::<16>::new_by_str("aabbcc");
1751 /// bytes2.replace(b"bb", b"X").unwrap();
1752 /// assert_eq!(bytes2.as_str(), "aaXcc");
1753 ///
1754 /// // Longer replacement
1755 /// let mut bytes3 = Bytes::<16>::new_by_str("Hi");
1756 /// bytes3.replace(b"Hi", b"Hello").unwrap();
1757 /// assert_eq!(bytes3.as_str(), "Hello");
1758 ///
1759 /// // Multiple occurrences
1760 /// let mut bytes4 = Bytes::<32>::new_by_str("foo bar foo");
1761 /// bytes4.replace(b"foo", b"baz").unwrap();
1762 /// assert_eq!(bytes4.as_str(), "baz bar baz");
1763 ///
1764 /// // Buffer overflow error
1765 /// let mut small = Bytes::<8>::new_by_str("Hello");
1766 /// assert!(small.replace(b"Hello", b"Hello World").is_err());
1767 /// ```
1768 pub fn replace(&mut self, find: &[u8], replace: &[u8]) -> Result<()> {
1769 if find.is_empty() {
1770 return Ok(());
1771 }
1772
1773 let mut i = 0;
1774 loop {
1775 let current_len = self.len();
1776
1777 // Exit if we've reached the end
1778 if i >= current_len {
1779 break;
1780 }
1781
1782 // Check if pattern starts at position i
1783 if i + find.len() <= current_len && self.0[i..i + find.len()] == *find {
1784 let remaining_len = current_len - (i + find.len());
1785 let new_len = i + replace.len() + remaining_len;
1786
1787 // Check if replacement fits in buffer
1788 if new_len > SIZE {
1789 return Err(Error::StringConversionError);
1790 }
1791
1792 // Shift remaining content if sizes differ
1793 if replace.len() != find.len() {
1794 self.0.copy_within(
1795 i + find.len()..i + find.len() + remaining_len,
1796 i + replace.len()
1797 );
1798 }
1799
1800 // Insert replacement bytes
1801 self.0[i..i + replace.len()].copy_from_slice(replace);
1802
1803 // Update null terminator position
1804 if new_len < SIZE {
1805 self.0[new_len] = 0;
1806 }
1807
1808 // Clear trailing bytes if content shrunk
1809 if new_len < current_len {
1810 for j in (new_len + 1)..=current_len {
1811 if j < SIZE {
1812 self.0[j] = 0;
1813 }
1814 }
1815 }
1816
1817 // Move past the replacement to avoid infinite loops
1818 i += replace.len();
1819 } else {
1820 i += 1;
1821 }
1822 }
1823
1824 Ok(())
1825 }
1826
1827 /// Converts the `Bytes` instance to a byte slice.
1828 ///
1829 /// This method provides a convenient way to access the internal byte array
1830 /// as a slice, which can be useful for C FFI or other operations that
1831 /// require byte slices.
1832 ///
1833 /// # Examples
1834 ///
1835 /// ```ignore
1836 /// use osal_rs::utils::{Bytes, ToBytes};
1837 ///
1838 /// let bytes = Bytes::<8>::new_by_str("example");
1839 /// let byte_slice = bytes.to_bytes();
1840 /// assert_eq!(byte_slice, b"example\0\0");
1841 /// ```
1842 #[inline]
1843 pub fn to_bytes(&self) -> &[u8] {
1844 &self.0
1845 }
1846
1847 /// Pops the last byte from the buffer and returns it.
1848 ///
1849 /// This method removes the last byte of content (before the first null terminator)
1850 /// 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.
1851 ///
1852 /// # Returns
1853 ///
1854 /// * `Some(u8)` - The last byte of content if the buffer is not empty
1855 /// * `None` - If the buffer is empty
1856 ///
1857 /// # Examples
1858 //// ```ignore
1859 /// use osal_rs::utils::Bytes;
1860 ///
1861 /// let mut bytes = Bytes::<16>::new_by_str("Hello");
1862 /// assert_eq!(bytes.pop(), Some(b'o'));
1863 /// assert_eq!(bytes.as_str(), "Hell");
1864 ///
1865 /// // Pop until empty
1866 /// assert_eq!(bytes.pop(), Some(b'l'));
1867 /// assert_eq!(bytes.pop(), Some(b'l'));
1868 /// assert_eq!(bytes.pop(), Some(b'e'));
1869 /// assert_eq!(bytes.pop(), Some(b'H'));
1870 /// assert_eq!(bytes.pop(), None);
1871 /// ```
1872 pub fn pop(&mut self) -> Option<u8> {
1873 let len = self.len();
1874 if len == 0 {
1875 None
1876 } else {
1877 let byte = self.0[len - 1];
1878 self.0[len - 1] = 0; // Clear the popped byte
1879 Some(byte)
1880 }
1881 }
1882
1883 /// Pushes a byte to the end of the content in the buffer.
1884 ///
1885 /// # Parameters
1886 ///
1887 /// * `byte` - The byte to push into the buffer
1888 ///
1889 /// # Returns
1890 ///
1891 /// * `Ok(())` - If the byte was successfully pushed
1892 /// * `Err(Error::StringConversionError)` - If the buffer is full
1893 ///
1894 /// # Examples
1895 ///
1896 /// ```ignore
1897 /// use osal_rs::utils::Bytes;
1898 ///
1899 /// let mut bytes = Bytes::<16>::new_by_str("Hello");
1900 /// assert_eq!(bytes.push(b'!'), Ok(()));
1901 /// assert_eq!(bytes.as_str(), "Hello!");
1902 /// ```
1903 pub fn push(&mut self, byte: u8) -> Result<()> {
1904 let len = self.len();
1905 if len >= SIZE {
1906 Err(Error::StringConversionError) // Buffer is full
1907 } else {
1908 self.0[len] = byte;
1909 Ok(())
1910 }
1911 }
1912
1913 /// Pops the last byte from the buffer and returns it as a character.
1914 ///
1915 /// This method removes the last byte of content (before the first null terminator)
1916 /// 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.
1917 ///
1918 /// # Returns
1919 ///
1920 /// * `Some(char)` - The last byte of content as a character if the buffer is not empty and the byte is a valid character
1921 /// * `None` - If the buffer is empty or if the byte cannot be converted to a valid character
1922 ///
1923 /// # Examples
1924 //// ```ignore
1925 /// use osal_rs::utils::Bytes;
1926 ///
1927 /// let mut bytes = Bytes::<16>::new_by_str("Hello");
1928 /// assert_eq!(bytes.pop_char(), Some('o'));
1929 /// assert_eq!(bytes.as_str(), "Hell");
1930 ///
1931 /// // Pop until empty
1932 /// assert_eq!(bytes.pop_char(), Some('l'));
1933 /// assert_eq!(bytes.pop_char(), Some('l'));
1934 /// assert_eq!(bytes.pop_char(), Some('e'));
1935 /// assert_eq!(bytes.pop_char(), Some('H'));
1936 /// assert_eq!(bytes.pop_char(), None);
1937 /// ```
1938 #[inline]
1939 pub fn pop_char(&mut self) -> Option<char> {
1940 self.pop().and_then(|byte| char::from_u32(byte as u32))
1941 }
1942
1943 /// Pushes a character to the end of the content in the buffer.
1944 ///
1945 /// 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.
1946 ///
1947 /// # Parameters
1948 ///
1949 /// * `ch` - The character to push into the buffer
1950 ///
1951 /// # Returns
1952 ///
1953 /// * `Ok(())` - If the character was successfully pushed
1954 /// * `Err(Error::StringConversionError)` - If the character is not a valid ASCII character or if the buffer is full
1955 ///
1956 /// # Examples
1957 //// ```ignore
1958 /// use osal_rs::utils::Bytes;
1959 ///
1960 /// let mut bytes = Bytes::<16>::new_by_str("Hello");
1961 /// assert_eq!(bytes.push_char('!'), Ok(()));
1962 /// assert_eq!(bytes.as_str(), "Hello!");
1963 ///
1964 /// // Attempt to push a non-ASCII character
1965 /// assert!(bytes.push_char('é').is_err());
1966 /// ```
1967 pub fn push_char(&mut self, ch: char) -> Result<()> {
1968 if ch.is_ascii() {
1969 self.push(ch as u8)
1970 } else {
1971 Err(Error::StringConversionError) // Non-ASCII characters not supported
1972 }
1973 }
1974
1975 /// Checks if the content of the buffer can be interpreted as a valid UTF-8 string.
1976 ///
1977 /// 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`.
1978 ///
1979 /// # Returns
1980 ///
1981 /// * `true` - If the content can be interpreted as a valid UTF-8 string
1982 /// * `false` - If the content contains invalid UTF-8 sequences
1983 ///
1984 /// # Examples
1985 //// ```ignore
1986 /// use osal_rs::utils::Bytes;
1987 ///
1988 /// let valid_bytes = Bytes::<16>::new_by_str("Hello");
1989 /// assert!(valid_bytes.is_string());
1990 ///
1991 /// let mut invalid_bytes = Bytes::<16>::new();
1992 /// invalid_bytes[0] = 0xFF; // Invalid UTF-8 byte
1993 /// assert!(!invalid_bytes.is_string());
1994 /// ```
1995 #[inline]
1996 pub fn is_string(&self) -> bool {
1997 String::from_utf8(self.0.to_vec()).is_ok()
1998 }
1999
2000 /// Returns the buffer content as a UTF-8 string slice.
2001 ///
2002 /// Interprets the byte array as a null-terminated C string and returns
2003 /// a `&str`. If the bytes contain invalid UTF-8, returns `"Conversion error"`.
2004 ///
2005 /// This is an inherent method (no trait import required at the call site).
2006 #[inline]
2007 pub fn as_str(&self) -> &str {
2008 unsafe {
2009 CStr::from_ptr(self.0.as_ptr() as *const c_char)
2010 .to_str()
2011 .unwrap_or("Bytes::as_str() Conversion error - invalid UTF-8")
2012 }
2013 }
2014
2015 /// Overwrites the buffer with a formatted string, behaving like `alloc::format!`.
2016 ///
2017 /// Clears the current content and fills the buffer with the result of formatting
2018 /// `args`. Content that exceeds `SIZE` is silently truncated.
2019 ///
2020 /// # Parameters
2021 ///
2022 /// * `args` - A [`core::fmt::Arguments`] value, typically created with [`format_args!`]
2023 ///
2024 /// # Examples
2025 ///
2026 /// ```ignore
2027 /// use osal_rs::utils::Bytes;
2028 ///
2029 /// let mut b = Bytes::<32>::new();
2030 /// b.format(format_args!("Hello {}", 42));
2031 /// assert_eq!(b.as_str(), "Hello 42");
2032 ///
2033 /// let mut b2 = Bytes::<8>::new();
2034 /// b2.format(format_args!("{:.2}", 3.14159));
2035 /// assert_eq!(b2.as_str(), "3.14");
2036 /// ```
2037 #[inline]
2038 pub fn format(&mut self, args: core::fmt::Arguments<'_>) {
2039 self.clear();
2040 let _ = core::fmt::write(self, args);
2041 }
2042
2043}
2044
2045/// Converts a byte slice to a hexadecimal string representation.
2046///
2047/// Each byte is converted to its two-character hexadecimal representation
2048/// in lowercase. This function allocates a new `String` on the heap.
2049///
2050/// # Parameters
2051///
2052/// * `bytes` - The byte slice to convert
2053///
2054/// # Returns
2055///
2056/// A `String` containing the hexadecimal representation of the bytes.
2057/// Each byte is represented by exactly 2 hex characters (lowercase).
2058///
2059/// # Memory Allocation
2060///
2061/// This function allocates heap memory. In memory-constrained environments,
2062/// consider using [`bytes_to_hex_into_slice`] instead.
2063///
2064/// # Examples
2065///
2066/// ```ignore
2067/// use osal_rs::utils::bytes_to_hex;
2068///
2069/// let data = &[0x01, 0x23, 0xAB, 0xFF];
2070/// let hex = bytes_to_hex(data);
2071/// assert_eq!(hex, "0123abff");
2072///
2073/// let empty = bytes_to_hex(&[]);
2074/// assert_eq!(empty, "");
2075/// ```
2076#[inline]
2077pub fn bytes_to_hex(bytes: &[u8]) -> String {
2078 bytes.iter()
2079 .map(|b| format!("{:02x}", b))
2080 .collect()
2081}
2082
2083/// Converts a byte slice to hexadecimal representation into a pre-allocated buffer.
2084///
2085/// This is a zero-allocation version of [`bytes_to_hex`] that writes the
2086/// hexadecimal representation directly into a provided output buffer.
2087/// Suitable for embedded systems and real-time applications.
2088///
2089/// # Parameters
2090///
2091/// * `bytes` - The source byte slice to convert
2092/// * `output` - The destination buffer to write hex characters into
2093///
2094/// # Returns
2095///
2096/// The number of bytes written to the output buffer (always `bytes.len() * 2`).
2097///
2098/// # Panics
2099///
2100/// Panics if `output.len() < bytes.len() * 2`. The output buffer must be
2101/// at least twice the size of the input to hold the hex representation.
2102///
2103/// # Examples
2104///
2105/// ```ignore
2106/// use osal_rs::utils::bytes_to_hex_into_slice;
2107///
2108/// let data = &[0x01, 0xAB, 0xFF];
2109/// let mut buffer = [0u8; 6];
2110///
2111/// let written = bytes_to_hex_into_slice(data, &mut buffer);
2112/// assert_eq!(written, 6);
2113/// assert_eq!(&buffer, b"01abff");
2114///
2115/// // Will panic - buffer too small
2116/// // let mut small = [0u8; 4];
2117/// // bytes_to_hex_into_slice(data, &mut small);
2118/// ```
2119pub fn bytes_to_hex_into_slice(bytes: &[u8], output: &mut [u8]) -> usize {
2120 assert!(output.len() >= bytes.len() * 2, "Buffer too small for hex conversion");
2121 let mut i = 0;
2122 for &b in bytes {
2123 let hex = format!("{:02x}", b);
2124 output[i..i+2].copy_from_slice(hex.as_bytes());
2125 i += 2;
2126 }
2127 i
2128}
2129
2130/// Converts a hexadecimal string to a vector of bytes.
2131///
2132/// Parses a string of hexadecimal digits (case-insensitive) and converts
2133/// them to their binary representation. Each pair of hex digits becomes
2134/// one byte in the output.
2135///
2136/// # Parameters
2137///
2138/// * `hex` - A string slice containing hexadecimal digits (0-9, a-f, A-F)
2139///
2140/// # Returns
2141///
2142/// * `Ok(Vec<u8>)` - A vector containing the decoded bytes
2143/// * `Err(Error::StringConversionError)` - If the string has odd length or contains invalid hex digits
2144///
2145/// # Memory Allocation
2146///
2147/// This function allocates a `Vec` on the heap. For no-alloc environments,
2148/// use [`hex_to_bytes_into_slice`] instead.
2149///
2150/// # Examples
2151///
2152/// ```ignore
2153/// use osal_rs::utils::hex_to_bytes;
2154///
2155/// // Lowercase hex
2156/// let bytes = hex_to_bytes("0123abff").unwrap();
2157/// assert_eq!(bytes, vec![0x01, 0x23, 0xAB, 0xFF]);
2158///
2159/// // Uppercase hex
2160/// let bytes2 = hex_to_bytes("ABCD").unwrap();
2161/// assert_eq!(bytes2, vec![0xAB, 0xCD]);
2162///
2163/// // Odd length - error
2164/// assert!(hex_to_bytes("ABC").is_err());
2165///
2166/// // Invalid character - error
2167/// assert!(hex_to_bytes("0G").is_err());
2168/// ```
2169pub fn hex_to_bytes(hex: &str) -> Result<Vec<u8>> {
2170 if hex.len() % 2 != 0 {
2171 return Err(Error::StringConversionError);
2172 }
2173
2174 let bytes_result: Result<Vec<u8>> = (0..hex.len())
2175 .step_by(2)
2176 .map(|i| {
2177 u8::from_str_radix(&hex[i..i + 2], 16)
2178 .map_err(|_| Error::StringConversionError)
2179 })
2180 .collect();
2181
2182 bytes_result
2183}
2184
2185/// Converts a hexadecimal string to bytes into a pre-allocated buffer.
2186///
2187/// This is a zero-allocation version of [`hex_to_bytes`] that writes decoded
2188/// bytes directly into a provided output buffer. Suitable for embedded systems
2189/// and real-time applications where heap allocation is not desired.
2190///
2191/// # Parameters
2192///
2193/// * `hex` - A string slice containing hexadecimal digits (0-9, a-f, A-F)
2194/// * `output` - The destination buffer to write decoded bytes into
2195///
2196/// # Returns
2197///
2198/// * `Ok(usize)` - The number of bytes written to the output buffer (`hex.len() / 2`)
2199/// * `Err(Error::StringConversionError)` - If:
2200/// - The hex string has odd length
2201/// - The output buffer is too small (`output.len() < hex.len() / 2`)
2202/// - The hex string contains invalid characters
2203///
2204/// # Examples
2205///
2206/// ```ignore
2207/// use osal_rs::utils::hex_to_bytes_into_slice;
2208///
2209/// let mut buffer = [0u8; 4];
2210/// let written = hex_to_bytes_into_slice("0123abff", &mut buffer).unwrap();
2211/// assert_eq!(written, 4);
2212/// assert_eq!(buffer, [0x01, 0x23, 0xAB, 0xFF]);
2213///
2214/// // Buffer too small
2215/// let mut small = [0u8; 2];
2216/// assert!(hex_to_bytes_into_slice("0123abff", &mut small).is_err());
2217///
2218/// // Odd length string
2219/// assert!(hex_to_bytes_into_slice("ABC", &mut buffer).is_err());
2220/// ```
2221pub fn hex_to_bytes_into_slice(hex: &str, output: &mut [u8]) -> Result<usize> {
2222 if hex.len() % 2 != 0 || output.len() < hex.len() / 2 {
2223 return Err(Error::StringConversionError);
2224 }
2225
2226 for i in 0..(hex.len() / 2) {
2227 output[i] = u8::from_str_radix(&hex[2 * i..2 * i + 2], 16)
2228 .map_err(|_| Error::StringConversionError)?;
2229 }
2230
2231 Ok(hex.len() / 2)
2232}