osal_rs/utils.rs
1/***************************************************************************
2 *
3 * osal-rs
4 * Copyright (C) 2023/2026 Antonio Salsi <passy.linux@zresa.it>
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 ***************************************************************************/
19
20//! Utility types and functions for OSAL-RS.
21//!
22//! This module contains common types, error definitions, and helper functions
23//! used throughout the library.
24
25use core::ffi::{CStr, c_char, c_void};
26use core::str::from_utf8_mut;
27use core::fmt::{Debug, Display};
28use core::ops::{Deref, DerefMut};
29use core::time::Duration;
30
31use alloc::ffi::CString;
32use alloc::format;
33use alloc::string::{String, ToString};
34use alloc::sync::Arc;
35use alloc::vec::Vec;
36
37#[cfg(not(feature = "serde"))]
38use crate::os::{Deserialize, Serialize};
39
40#[cfg(feature = "serde")]
41use osal_rs_serde::{Deserialize, Serialize};
42
43use crate::os::{AsSyncStr, Mutex};
44
45/// Error types for OSAL-RS operations.
46///
47/// Represents all possible error conditions that can occur when using
48/// the OSAL-RS library.
49///
50/// # Examples
51///
52/// ```ignore
53/// use osal_rs::os::{Queue, QueueFn};
54/// use osal_rs::utils::Error;
55///
56/// match Queue::new(10, 32) {
57/// Ok(queue) => { /* use queue */ },
58/// Err(Error::OutOfMemory) => println!("Failed to allocate queue"),
59/// Err(e) => println!("Other error: {:?}", e),
60/// }
61/// ```
62#[derive(Debug, Clone, PartialEq, Eq, Hash)]
63pub enum Error {
64 /// Insufficient memory to complete operation
65 OutOfMemory,
66 /// Queue send operation timed out
67 QueueSendTimeout,
68 /// Queue receive operation timed out
69 QueueReceiveTimeout,
70 /// Mutex operation timed out
71 MutexTimeout,
72 /// Failed to acquire mutex lock
73 MutexLockFailed,
74 /// Generic timeout error
75 Timeout,
76 /// Queue is full and cannot accept more items
77 QueueFull,
78 /// String conversion failed
79 StringConversionError,
80 /// Thread/task not found
81 TaskNotFound,
82 /// Invalid queue size specified
83 InvalidQueueSize,
84 /// Null pointer encountered
85 NullPtr,
86 /// Requested item not found
87 NotFound,
88 /// Index out of bounds
89 OutOfIndex,
90 /// Invalid type for operation
91 InvalidType,
92 /// No data available
93 Empty,
94 /// Write error occurred
95 WriteError,
96 /// Read error occurred
97 ReadError,
98 /// Return error with code
99 ReturnWithCode(i32),
100 /// Unhandled error with description
101 Unhandled(&'static str)
102}
103
104/// CPU register size enumeration.
105///
106/// Identifies whether the target CPU uses 32-bit or 64-bit registers.
107/// This is used for platform-specific tick count overflow handling.
108#[derive(PartialEq, Eq, Clone, Copy, Debug)]
109pub enum CpuRegisterSize {
110 /// 64-bit CPU registers
111 Bit64,
112 /// 32-bit CPU registers
113 Bit32
114}
115
116/// Boolean type compatible with RTOS return values.
117///
118/// Many RTOS functions return 0 for success and non-zero for failure.
119/// This type provides a Rust-idiomatic way to work with such values.
120///
121/// # Examples
122///
123/// ```ignore
124/// use osal_rs::os::{Semaphore, SemaphoreFn};
125/// use osal_rs::utils::OsalRsBool;
126/// use core::time::Duration;
127///
128/// let sem = Semaphore::new(1, 1).unwrap();
129///
130/// match sem.wait(Duration::from_millis(100)) {
131/// OsalRsBool::True => println!("Acquired semaphore"),
132/// OsalRsBool::False => println!("Failed to acquire"),
133/// }
134///
135/// // Can also convert to bool
136/// if sem.signal().into() {
137/// println!("Semaphore signaled");
138/// }
139/// ```
140#[derive(PartialEq, Eq, Clone, Copy, Debug)]
141#[repr(u8)]
142pub enum OsalRsBool {
143 /// Operation failed or condition is false
144 False = 1,
145 /// Operation succeeded or condition is true
146 True = 0
147}
148
149/// Maximum delay constant for blocking operations.
150///
151/// When used as a timeout parameter, indicates the operation should
152/// block indefinitely until it succeeds.
153///
154/// # Examples
155///
156/// ```ignore
157/// use osal_rs::os::{Mutex, MutexFn};
158/// use osal_rs::utils::MAX_DELAY;
159///
160/// let mutex = Mutex::new(0);
161/// let guard = mutex.lock(); // Blocks forever if needed
162/// ```
163pub const MAX_DELAY: Duration = Duration::from_millis(usize::MAX as u64);
164
165/// Standard Result type for OSAL-RS operations.
166///
167/// Uses [`Error`] as the default error type.
168pub type Result<T, E = Error> = core::result::Result<T, E>;
169
170/// Pointer to pointer type for C FFI.
171pub type DoublePtr = *mut *mut c_void;
172
173/// Mutable pointer type for C FFI.
174pub type Ptr = *mut c_void;
175
176/// Const pointer type for C FFI.
177pub type ConstPtr = *const c_void;
178
179/// Shortcut for Arc<Mutex<T>>
180pub type ArcMux<T> = Arc<Mutex<T>>;
181
182/// Determines the CPU register size at compile time.
183///
184/// This constant function checks the size of `usize` to determine whether
185/// the target architecture uses 32-bit or 64-bit registers. This information
186/// is used for platform-specific optimizations and overflow handling.
187///
188/// # Returns
189///
190/// * [`CpuRegisterSize::Bit64`] - For 64-bit architectures
191/// * [`CpuRegisterSize::Bit32`] - For 32-bit architectures
192///
193/// # Examples
194///
195/// ```ignore
196/// use osal_rs::utils::{register_bit_size, CpuRegisterSize};
197///
198/// match register_bit_size() {
199/// CpuRegisterSize::Bit64 => println!("Running on 64-bit platform"),
200/// CpuRegisterSize::Bit32 => println!("Running on 32-bit platform"),
201/// }
202/// ```
203pub const fn register_bit_size() -> CpuRegisterSize {
204 if size_of::<usize>() == 8 {
205 CpuRegisterSize::Bit64
206 } else {
207 CpuRegisterSize::Bit32
208 }
209}
210
211/// Converts a C string pointer to a Rust String.
212///
213/// This macro safely converts a raw C string pointer (`*const c_char`) into
214/// a Rust `String`. It handles UTF-8 conversion gracefully using lossy conversion.
215///
216/// # Safety
217///
218/// The pointer must be valid and point to a null-terminated C string.
219///
220/// # Examples
221///
222/// ```ignore
223/// use osal_rs::from_c_str;
224/// use core::ffi::c_char;
225///
226/// extern "C" {
227/// fn get_system_name() -> *const c_char;
228/// }
229///
230/// let name = from_c_str!(get_system_name());
231/// println!("System: {}", name);
232/// ```
233#[macro_export]
234macro_rules! from_c_str {
235 ($str:expr) => {
236 unsafe {
237 let c_str = core::ffi::CStr::from_ptr($str);
238 alloc::string::String::from_utf8_lossy(c_str.to_bytes()).to_string()
239 }
240 };
241}
242
243/// Converts a Rust string to a CString with error handling.
244///
245/// This macro creates a `CString` from a Rust string reference, returning
246/// a `Result` that can be used with the `?` operator. If the conversion fails
247/// (e.g., due to interior null bytes), it returns an appropriate error.
248///
249/// # Returns
250///
251/// * `Ok(CString)` - On successful conversion
252/// * `Err(Error::Unhandled)` - If the string contains interior null bytes
253///
254/// # Examples
255///
256/// ```ignore
257/// use osal_rs::to_cstring;
258/// use osal_rs::utils::Result;
259///
260/// fn pass_to_c_api(name: &str) -> Result<()> {
261/// let c_name = to_cstring!(name)?;
262/// // Use c_name.as_ptr() with C FFI
263/// Ok(())
264/// }
265/// ```
266#[macro_export]
267macro_rules! to_cstring {
268 ($s:expr) => {
269 alloc::ffi::CString::new($s.as_str())
270 .map_err(|_| $crate::utils::Error::Unhandled("Failed to convert string to CString"))
271 };
272}
273
274/// Converts a Rust string to a C string pointer.
275///
276/// This macro creates a `CString` from a Rust string and returns its raw pointer.
277/// **Warning**: This macro panics if the conversion fails. Consider using
278/// [`to_cstring!`] for safer error handling.
279///
280/// # Panics
281///
282/// Panics if the string contains interior null bytes.
283///
284/// # Examples
285///
286/// ```ignore
287/// use osal_rs::to_c_str;
288///
289/// extern "C" {
290/// fn set_name(name: *const core::ffi::c_char);
291/// }
292///
293/// let name = "FreeRTOS Task";
294/// unsafe {
295/// set_name(to_c_str!(name));
296/// }
297/// ```
298#[macro_export]
299macro_rules! to_c_str {
300 ($s:expr) => {
301 alloc::ffi::CString::new($s.as_ref() as &str).unwrap().as_ptr()
302 };
303}
304
305/// Converts a string to a fixed-size byte array.
306///
307/// This macro creates a byte array of the specified size and fills it with
308/// the bytes from the input string. If the string is shorter than the buffer,
309/// the remaining bytes are filled with spaces. If the string is longer, it
310/// is truncated to fit.
311///
312/// # Parameters
313///
314/// * `$str` - The source string to convert
315/// * `$buff_name` - The identifier name for the created buffer variable
316/// * `$buff_size` - The size of the byte array to create
317///
318/// # Examples
319///
320/// ```ignore
321/// use osal_rs::from_str_to_array;
322///
323/// let task_name = "MainTask";
324/// from_str_to_array!(task_name, name_buffer, 16);
325/// // name_buffer is now [u8; 16] containing "MainTask "
326///
327/// // Use with C FFI
328/// extern "C" {
329/// fn create_task(name: *const u8, len: usize);
330/// }
331///
332/// unsafe {
333/// create_task(name_buffer.as_ptr(), name_buffer.len());
334/// }
335/// ```
336#[macro_export]
337macro_rules! from_str_to_array {
338 ($str:expr, $buff_name:ident, $buff_size:expr) => {
339 let mut $buff_name = [b' '; $buff_size];
340 let _bytes = $str.as_bytes();
341 let _len = core::cmp::min(_bytes.len(), $buff_size);
342 $buff_name[.._len].copy_from_slice(&_bytes[.._len]);
343 };
344}
345
346/// Extracts a typed parameter from an optional boxed Any reference.
347///
348/// This macro is used in thread/task entry points to safely extract and
349/// downcast parameters passed to the thread. It handles both the Option
350/// unwrapping and the type downcast, returning appropriate errors if either
351/// operation fails.
352///
353/// # Parameters
354///
355/// * `$param` - An `Option<Box<dyn Any>>` containing the parameter
356/// * `$t` - The type to downcast the parameter to
357///
358/// # Returns
359///
360/// * A reference to the downcasted value of type `$t`
361/// * `Err(Error::NullPtr)` - If the parameter is None
362/// * `Err(Error::InvalidType)` - If the downcast fails
363///
364/// # Examples
365///
366/// ```ignore
367/// use osal_rs::thread_extract_param;
368/// use osal_rs::utils::Result;
369/// use core::any::Any;
370///
371/// struct TaskConfig {
372/// priority: u8,
373/// stack_size: usize,
374/// }
375///
376/// fn task_entry(param: Option<Box<dyn Any>>) -> Result<()> {
377/// let config = thread_extract_param!(param, TaskConfig);
378///
379/// println!("Priority: {}", config.priority);
380/// println!("Stack: {}", config.stack_size);
381///
382/// Ok(())
383/// }
384/// ```
385#[macro_export]
386macro_rules! thread_extract_param {
387 ($param:expr, $t:ty) => {
388 match $param.as_ref() {
389 Some(p) => {
390 match p.downcast_ref::<$t>() {
391 Some(value) => value,
392 None => return Err($crate::utils::Error::InvalidType),
393 }
394 }
395 None => return Err($crate::utils::Error::NullPtr),
396 }
397 };
398}
399
400/// Creates an Arc<Mutex<T>> from a value.
401///
402/// This is a convenience macro to reduce boilerplate when creating
403/// thread-safe shared data structures.
404///
405/// # Examples
406///
407/// ```ignore
408/// use osal_rs::arcmux;
409///
410/// let shared_counter = arcmux!(0);
411/// // Equivalent to: Arc::new(Mutex::new(0))
412/// ```
413#[macro_export]
414macro_rules! arcmux {
415 ($value:expr) => {
416 alloc::sync::Arc::new($crate::os::Mutex::new($value))
417 };
418}
419
420
421
422/// Fixed-size byte array wrapper with string conversion utilities.
423///
424/// `Bytes` is a generic wrapper around a fixed-size byte array that provides
425/// convenient methods for converting between strings and byte arrays. It's
426/// particularly useful for interfacing with C APIs that expect fixed-size
427/// character buffers, or for storing strings in embedded systems with
428/// constrained memory.
429///
430/// # Type Parameters
431///
432/// * `SIZE` - The size of the internal byte array (default: 0)
433///
434/// # Examples
435///
436/// ```ignore
437/// use osal_rs::utils::Bytes;
438///
439/// // Create an empty 32-byte buffer
440/// let mut buffer = Bytes::<32>::new();
441///
442/// // Create a buffer from a string
443/// let name = Bytes::<16>::new_by_str("TaskName");
444/// println!("{}", name); // Prints "TaskName"
445///
446/// // Create from any type that implements ToString
447/// let number = 42;
448/// let num_bytes = Bytes::<8>::new_by_string(&number);
449/// ```
450
451#[cfg(feature = "serde")]
452#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
453pub struct Bytes<const SIZE: usize> (pub [u8; SIZE]);
454
455#[cfg(not(feature = "serde"))]
456#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
457pub struct Bytes<const SIZE: usize> (pub [u8; SIZE]);
458
459impl<const SIZE: usize> Deref for Bytes<SIZE> {
460 type Target = [u8; SIZE];
461
462 /// Dereferences to the underlying byte array.
463 ///
464 /// This allows `Bytes` to be used anywhere a `[u8; SIZE]` reference is expected.
465 ///
466 /// # Examples
467 ///
468 /// ```ignore
469 /// use osal_rs::utils::Bytes;
470 ///
471 /// let bytes = Bytes::<8>::new_by_str("test");
472 /// assert_eq!(bytes[0], b't');
473 /// ```
474 fn deref(&self) -> &Self::Target {
475 &self.0
476 }
477}
478
479impl<const SIZE: usize> DerefMut for Bytes<SIZE> {
480 /// Provides mutable access to the underlying byte array.
481 ///
482 /// This allows `Bytes` to be mutably dereferenced, enabling direct modification
483 /// of the internal byte array through the `DerefMut` trait.
484 ///
485 /// # Examples
486 ///
487 /// ```ignore
488 /// use osal_rs::utils::Bytes;
489 ///
490 /// let mut bytes = Bytes::<8>::new();
491 /// bytes[0] = b'H';
492 /// bytes[1] = b'i';
493 /// assert_eq!(bytes[0], b'H');
494 /// ```
495 fn deref_mut(&mut self) -> &mut Self::Target {
496 &mut self.0
497 }
498}
499
500impl<const SIZE: usize> Display for Bytes<SIZE> {
501 /// Formats the byte array as a C-style null-terminated string.
502 ///
503 /// This implementation treats the byte array as a C string and converts it
504 /// to a Rust string for display. If the conversion fails, it displays
505 /// "Conversion error".
506 ///
507 /// # Safety
508 ///
509 /// This method assumes the byte array contains valid UTF-8 data and is
510 /// null-terminated. Invalid data may result in the error message being displayed.
511 ///
512 /// # Examples
513 ///
514 /// ```ignore
515 /// use osal_rs::utils::Bytes;
516 ///
517 /// let bytes = Bytes::<16>::new_by_str("Hello");
518 /// println!("{}", bytes); // Prints "Hello"
519 /// ```
520 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
521 let str = unsafe {
522 CStr::from_ptr(self.0.as_ptr() as *const c_char)
523 .to_str()
524 .unwrap_or("Conversion error")
525 };
526
527 write!(f, "{}", str.to_string())
528 }
529}
530
531impl<const SIZE: usize> AsSyncStr for Bytes<SIZE> {
532 /// Returns a string slice reference.
533 ///
534 /// This method provides access to the underlying string data in a way
535 /// that is safe to use across thread boundaries.
536 ///
537 /// # Returns
538 ///
539 /// A reference to a string slice with lifetime tied to `self`.
540 fn as_str(&self) -> &str {
541 unsafe {
542 CStr::from_ptr(self.0.as_ptr() as *const c_char)
543 .to_str()
544 .unwrap_or("Conversion error")
545 }
546 }
547}
548
549/// Serialization implementation for `Bytes<SIZE>` when the `serde` feature is enabled.
550///
551/// This implementation provides serialization by directly serializing each byte
552/// in the array using the osal-rs-serde serialization framework.
553#[cfg(feature = "serde")]
554impl<const SIZE: usize> Serialize for Bytes<SIZE> {
555 /// Serializes the `Bytes` instance using the given serializer.
556 ///
557 /// # Parameters
558 ///
559 /// * `serializer` - The serializer to use
560 ///
561 /// # Returns
562 ///
563 /// * `Ok(())` - On successful serialization
564 /// * `Err(S::Error)` - If serialization fails
565 fn serialize<S: osal_rs_serde::Serializer>(&self, serializer: &mut S) -> core::result::Result<(), S::Error> {
566 for &byte in self.0.iter() {
567 serializer.serialize_u8("", byte)?;
568 }
569 Ok(())
570 }
571}
572
573/// Deserialization implementation for `Bytes<SIZE>` when the `serde` feature is enabled.
574///
575/// This implementation provides deserialization by reading bytes from the deserializer
576/// into a fixed-size array using the osal-rs-serde deserialization framework.
577#[cfg(feature = "serde")]
578impl<const SIZE: usize> Deserialize for Bytes<SIZE> {
579 /// Deserializes a `Bytes` instance using the given deserializer.
580 ///
581 /// # Parameters
582 ///
583 /// * `deserializer` - The deserializer to use
584 ///
585 /// # Returns
586 ///
587 /// * `Ok(Bytes<SIZE>)` - A new `Bytes` instance with deserialized data
588 /// * `Err(D::Error)` - If deserialization fails
589 fn deserialize<D: osal_rs_serde::Deserializer>(deserializer: &mut D, name: &str) -> core::result::Result<Self, D::Error> {
590 let mut array = [0u8; SIZE];
591 for i in 0..SIZE {
592 array[i] = deserializer.deserialize_u8(name)?;
593 }
594 Ok(Self(array))
595 }
596}
597
598/// Serialization implementation for `Bytes<SIZE>` when the `serde` feature is disabled.
599///
600/// This implementation provides basic serialization by directly returning a reference
601/// to the underlying byte array. It's used when the library is compiled without the
602/// `serde` feature, providing a lightweight alternative serialization mechanism.
603#[cfg(not(feature = "serde"))]
604impl<const SIZE: usize> Serialize for Bytes<SIZE> {
605 /// Converts the `Bytes` instance to a byte slice.
606 ///
607 /// # Returns
608 ///
609 /// A reference to the internal byte array.
610 fn to_bytes(&self) -> &[u8] {
611 &self.0
612 }
613}
614
615/// Deserialization implementation for `Bytes<SIZE>` when the `serde` feature is disabled.
616///
617/// This implementation provides basic deserialization by copying bytes from a slice
618/// into a fixed-size array. If the source slice is shorter than `SIZE`, the remaining
619/// bytes are zero-filled. If longer, it's truncated to fit.
620#[cfg(not(feature = "serde"))]
621impl<const SIZE: usize> Deserialize for Bytes<SIZE> {
622 /// Creates a `Bytes` instance from a byte slice.
623 ///
624 /// # Parameters
625 ///
626 /// * `bytes` - The source byte slice to deserialize from
627 ///
628 /// # Returns
629 ///
630 /// * `Ok(Bytes<SIZE>)` - A new `Bytes` instance with data copied from the slice
631 ///
632 /// # Examples
633 ///
634 /// ```ignore
635 /// use osal_rs::utils::Bytes;
636 /// use osal_rs::os::Deserialize;
637 ///
638 /// let data = b"Hello";
639 /// let bytes = Bytes::<16>::from_bytes(data).unwrap();
640 /// // Result: [b'H', b'e', b'l', b'l', b'o', 0, 0, 0, ...]
641 /// ```
642 fn from_bytes(bytes: &[u8]) -> Result<Self> {
643 let mut array = [0u8; SIZE];
644 let len = core::cmp::min(bytes.len(), SIZE);
645 array[..len].copy_from_slice(&bytes[..len]);
646 Ok(Self( array ))
647 }
648}
649
650impl<const SIZE: usize> Bytes<SIZE> {
651 /// Creates a new `Bytes` instance filled with zeros.
652 ///
653 /// This is a const function, allowing it to be used in const contexts
654 /// and static variable declarations.
655 ///
656 /// # Returns
657 ///
658 /// A `Bytes` instance with all bytes set to 0.
659 ///
660 /// # Examples
661 ///
662 /// ```ignore
663 /// use osal_rs::utils::Bytes;
664 ///
665 /// const BUFFER: Bytes<64> = Bytes::new();
666 ///
667 /// let runtime_buffer = Bytes::<32>::new();
668 /// assert_eq!(runtime_buffer[0], 0);
669 /// ```
670 pub const fn new() -> Self {
671 Self( [0u8; SIZE] )
672 }
673
674 /// Creates a new `Bytes` instance from a string slice.
675 ///
676 /// Copies the bytes from the input string into the fixed-size array.
677 /// If the string is shorter than `SIZE`, the remaining bytes are zero-filled.
678 /// If the string is longer, it is truncated to fit.
679 ///
680 /// # Parameters
681 ///
682 /// * `str` - The source string to convert
683 ///
684 /// # Returns
685 ///
686 /// A `Bytes` instance containing the string data.
687 ///
688 /// # Examples
689 ///
690 /// ```ignore
691 /// use osal_rs::utils::Bytes;
692 ///
693 /// let short = Bytes::<16>::new_by_str("Hi");
694 /// // Internal array: [b'H', b'i', 0, 0, 0, ...]
695 ///
696 /// let exact = Bytes::<5>::new_by_str("Hello");
697 /// // Internal array: [b'H', b'e', b'l', b'l', b'o']
698 ///
699 /// let long = Bytes::<3>::new_by_str("Hello");
700 /// // Internal array: [b'H', b'e', b'l'] (truncated)
701 /// ```
702 pub fn new_by_str(str: &str) -> Self {
703
704 let mut array = [0u8; SIZE];
705
706 let mut i = 0usize ;
707 for byte in str.as_bytes() {
708 if i > SIZE - 1{
709 break;
710 }
711 array[i] = *byte;
712 i += 1;
713 }
714
715 Self( array )
716 }
717
718 /// Creates a new `Bytes` instance from any type implementing `ToString`.
719 ///
720 /// This is a convenience wrapper around [`new_by_str`](Self::new_by_str)
721 /// that first converts the input to a string.
722 ///
723 /// # Parameters
724 ///
725 /// * `str` - Any value that implements `ToString`
726 ///
727 /// # Returns
728 ///
729 /// A `Bytes` instance containing the string representation of the input.
730 ///
731 /// # Examples
732 ///
733 /// ```ignore
734 /// use osal_rs::utils::Bytes;
735 ///
736 /// // From integer
737 /// let num_bytes = Bytes::<8>::new_by_string(&42);
738 ///
739 /// // From String
740 /// let string = String::from("Task");
741 /// let str_bytes = Bytes::<16>::new_by_string(&string);
742 ///
743 /// // From custom type with ToString
744 /// #[derive(Debug)]
745 /// struct TaskId(u32);
746 /// impl ToString for TaskId {
747 /// fn to_string(&self) -> String {
748 /// format!("Task-{}", self.0)
749 /// }
750 /// }
751 /// let task_bytes = Bytes::<16>::new_by_string(&TaskId(5));
752 /// ```
753 pub fn new_by_string(str: &impl ToString) -> Self {
754 Self::new_by_str(&str.to_string())
755 }
756
757 /// Fills a mutable string slice with the contents of the byte array.
758 ///
759 /// Attempts to convert the internal byte array to a UTF-8 string and
760 /// copies it into the destination string slice. Only copies up to the
761 /// minimum of the source and destination lengths.
762 ///
763 /// # Parameters
764 ///
765 /// * `dest` - The destination string slice to fill
766 ///
767 /// # Panics
768 ///
769 /// Currently panics (todo!) if the byte array contains invalid UTF-8.
770 ///
771 /// # Examples
772 ///
773 /// ```ignore
774 /// use osal_rs::utils::Bytes;
775 ///
776 /// let bytes = Bytes::<16>::new_by_str("Hello World");
777 ///
778 /// let mut output = String::from(" "); // 16 spaces
779 /// bytes.fill_str(unsafe { output.as_mut_str() });
780 ///
781 /// assert_eq!(&output[..11], "Hello World");
782 /// ```
783 pub fn fill_str(&mut self, dest: &mut str) {
784 match from_utf8_mut(&mut self.0) {
785 Ok(str) => {
786 let len = core::cmp::min(str.len(), dest.len());
787 unsafe {
788 dest.as_bytes_mut()[..len].copy_from_slice(&str.as_bytes()[..len]);
789 }
790 }
791 Err(_) => todo!(),
792 }
793 }
794
795 /// Converts the byte array to a C string reference.
796 ///
797 /// Creates a `CStr` reference from the internal byte array, treating it as
798 /// a null-terminated C string. This is useful for passing strings to C FFI
799 /// functions that expect `*const c_char` or `&CStr`.
800 ///
801 /// # Safety
802 ///
803 /// This method is unsafe because it assumes:
804 /// - The byte array contains valid UTF-8 data
805 /// - The byte array is null-terminated
806 /// - There are no interior null bytes before the terminating null
807 ///
808 /// Violating these assumptions may lead to undefined behavior.
809 ///
810 /// # Returns
811 ///
812 /// A reference to a `CStr` with lifetime tied to `self`.
813 ///
814 /// # Examples
815 ///
816 /// ```ignore
817 /// use osal_rs::utils::Bytes;
818 ///
819 /// let bytes = Bytes::<16>::new_by_str("Hello");
820 /// let c_str = bytes.as_c_str();
821 ///
822 /// extern "C" {
823 /// fn print_string(s: *const core::ffi::c_char);
824 /// }
825 ///
826 /// unsafe {
827 /// print_string(c_str.as_ptr());
828 /// }
829 /// ```
830 pub fn as_c_str(&self) -> &CStr {
831 unsafe {
832 CStr::from_ptr(self.0.as_ptr() as *const c_char)
833 }
834 }
835
836 /// Converts the byte array to an owned C string.
837 ///
838 /// Creates a new `CString` by copying the contents of the internal byte array.
839 /// Unlike [`as_c_str`](Self::as_c_str), this method allocates heap memory and
840 /// returns an owned string that can outlive the original `Bytes` instance.
841 ///
842 /// # Safety
843 ///
844 /// This method uses `from_vec_unchecked` which assumes the byte array
845 /// does not contain any interior null bytes. If this assumption is violated,
846 /// the resulting `CString` will be invalid.
847 ///
848 /// # Returns
849 ///
850 /// An owned `CString` containing a copy of the byte array data.
851 ///
852 /// # Memory Allocation
853 ///
854 /// This method allocates on the heap. In memory-constrained embedded systems,
855 /// prefer [`as_c_str`](Self::as_c_str) when possible.
856 ///
857 /// # Examples
858 ///
859 /// ```ignore
860 /// use osal_rs::utils::Bytes;
861 ///
862 /// fn process_name(bytes: &Bytes<16>) -> alloc::ffi::CString {
863 /// // Create an owned copy that can be returned
864 /// bytes.as_cstring()
865 /// }
866 ///
867 /// let name = Bytes::<16>::new_by_str("Task");
868 /// let owned = process_name(&name);
869 /// // 'name' can be dropped, 'owned' still valid
870 /// ```
871 pub fn as_cstring(&self) -> CString {
872 unsafe {
873 CString::from_vec_unchecked(self.0.to_vec())
874 }
875 }
876}
877
878/// Converts a byte slice to a hexadecimal string representation.
879///
880/// Each byte is converted to its two-character hexadecimal representation
881/// in lowercase. This function allocates a new `String` on the heap.
882///
883/// # Parameters
884///
885/// * `bytes` - The byte slice to convert
886///
887/// # Returns
888///
889/// A `String` containing the hexadecimal representation of the bytes.
890/// Each byte is represented by exactly 2 hex characters (lowercase).
891///
892/// # Memory Allocation
893///
894/// This function allocates heap memory. In memory-constrained environments,
895/// consider using [`bytes_to_hex_into_slice`] instead.
896///
897/// # Examples
898///
899/// ```ignore
900/// use osal_rs::utils::bytes_to_hex;
901///
902/// let data = &[0x01, 0x23, 0xAB, 0xFF];
903/// let hex = bytes_to_hex(data);
904/// assert_eq!(hex, "0123abff");
905///
906/// let empty = bytes_to_hex(&[]);
907/// assert_eq!(empty, "");
908/// ```
909pub fn bytes_to_hex<'a>(bytes: &'a [u8]) -> String {
910 bytes.iter()
911 .map(|b| format!("{:02x}", b))
912 .collect()
913}
914
915/// Converts a byte slice to hexadecimal representation into a pre-allocated buffer.
916///
917/// This is a zero-allocation version of [`bytes_to_hex`] that writes the
918/// hexadecimal representation directly into a provided output buffer.
919/// Suitable for embedded systems and real-time applications.
920///
921/// # Parameters
922///
923/// * `bytes` - The source byte slice to convert
924/// * `output` - The destination buffer to write hex characters into
925///
926/// # Returns
927///
928/// The number of bytes written to the output buffer (always `bytes.len() * 2`).
929///
930/// # Panics
931///
932/// Panics if `output.len() < bytes.len() * 2`. The output buffer must be
933/// at least twice the size of the input to hold the hex representation.
934///
935/// # Examples
936///
937/// ```ignore
938/// use osal_rs::utils::bytes_to_hex_into_slice;
939///
940/// let data = &[0x01, 0xAB, 0xFF];
941/// let mut buffer = [0u8; 6];
942///
943/// let written = bytes_to_hex_into_slice(data, &mut buffer);
944/// assert_eq!(written, 6);
945/// assert_eq!(&buffer, b"01abff");
946///
947/// // Will panic - buffer too small
948/// // let mut small = [0u8; 4];
949/// // bytes_to_hex_into_slice(data, &mut small);
950/// ```
951pub fn bytes_to_hex_into_slice(bytes: &[u8], output: &mut [u8]) -> usize {
952 assert!(output.len() >= bytes.len() * 2, "Buffer too small for hex conversion");
953 let mut i = 0;
954 for &b in bytes {
955 let hex = format!("{:02x}", b);
956 output[i..i+2].copy_from_slice(hex.as_bytes());
957 i += 2;
958 }
959 i
960}
961
962/// Converts a hexadecimal string to a vector of bytes.
963///
964/// Parses a string of hexadecimal digits (case-insensitive) and converts
965/// them to their binary representation. Each pair of hex digits becomes
966/// one byte in the output.
967///
968/// # Parameters
969///
970/// * `hex` - A string slice containing hexadecimal digits (0-9, a-f, A-F)
971///
972/// # Returns
973///
974/// * `Ok(Vec<u8>)` - A vector containing the decoded bytes
975/// * `Err(Error::StringConversionError)` - If the string has odd length or contains invalid hex digits
976///
977/// # Memory Allocation
978///
979/// This function allocates a `Vec` on the heap. For no-alloc environments,
980/// use [`hex_to_bytes_into_slice`] instead.
981///
982/// # Examples
983///
984/// ```ignore
985/// use osal_rs::utils::hex_to_bytes;
986///
987/// // Lowercase hex
988/// let bytes = hex_to_bytes("0123abff").unwrap();
989/// assert_eq!(bytes, vec![0x01, 0x23, 0xAB, 0xFF]);
990///
991/// // Uppercase hex
992/// let bytes2 = hex_to_bytes("ABCD").unwrap();
993/// assert_eq!(bytes2, vec![0xAB, 0xCD]);
994///
995/// // Odd length - error
996/// assert!(hex_to_bytes("ABC").is_err());
997///
998/// // Invalid character - error
999/// assert!(hex_to_bytes("0G").is_err());
1000/// ```
1001pub fn hex_to_bytes(hex: &str) -> Result<Vec<u8>> {
1002 if hex.len() % 2 != 0 {
1003 return Err(Error::StringConversionError);
1004 }
1005
1006 let bytes_result: Result<Vec<u8>> = (0..hex.len())
1007 .step_by(2)
1008 .map(|i| {
1009 u8::from_str_radix(&hex[i..i + 2], 16)
1010 .map_err(|_| Error::StringConversionError)
1011 })
1012 .collect();
1013
1014 bytes_result
1015}
1016
1017/// Converts a hexadecimal string to bytes into a pre-allocated buffer.
1018///
1019/// This is a zero-allocation version of [`hex_to_bytes`] that writes decoded
1020/// bytes directly into a provided output buffer. Suitable for embedded systems
1021/// and real-time applications where heap allocation is not desired.
1022///
1023/// # Parameters
1024///
1025/// * `hex` - A string slice containing hexadecimal digits (0-9, a-f, A-F)
1026/// * `output` - The destination buffer to write decoded bytes into
1027///
1028/// # Returns
1029///
1030/// * `Ok(usize)` - The number of bytes written to the output buffer (`hex.len() / 2`)
1031/// * `Err(Error::StringConversionError)` - If:
1032/// - The hex string has odd length
1033/// - The output buffer is too small (`output.len() < hex.len() / 2`)
1034/// - The hex string contains invalid characters
1035///
1036/// # Examples
1037///
1038/// ```ignore
1039/// use osal_rs::utils::hex_to_bytes_into_slice;
1040///
1041/// let mut buffer = [0u8; 4];
1042/// let written = hex_to_bytes_into_slice("0123abff", &mut buffer).unwrap();
1043/// assert_eq!(written, 4);
1044/// assert_eq!(buffer, [0x01, 0x23, 0xAB, 0xFF]);
1045///
1046/// // Buffer too small
1047/// let mut small = [0u8; 2];
1048/// assert!(hex_to_bytes_into_slice("0123abff", &mut small).is_err());
1049///
1050/// // Odd length string
1051/// assert!(hex_to_bytes_into_slice("ABC", &mut buffer).is_err());
1052/// ```
1053pub fn hex_to_bytes_into_slice(hex: &str, output: &mut [u8]) -> Result<usize> {
1054 if hex.len() % 2 != 0 || output.len() < hex.len() / 2 {
1055 return Err(Error::StringConversionError);
1056 }
1057
1058 for i in 0..(hex.len() / 2) {
1059 output[i] = u8::from_str_radix(&hex[2 * i..2 * i + 2], 16)
1060 .map_err(|_| Error::StringConversionError)?;
1061 }
1062
1063 Ok(hex.len() / 2)
1064}