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