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