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