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};
26use core::{ffi::c_void, str::from_utf8_mut};
27use core::fmt::{Debug, Display}; 
28use core::ops::Deref;
29use core::time::Duration;
30use alloc::string::{String, ToString};
31use alloc::sync::Arc;
32
33use crate::os::Mutex;
34
35/// Error types for OSAL-RS operations.
36///
37/// Represents all possible error conditions that can occur when using
38/// the OSAL-RS library.
39///
40/// # Examples
41///
42/// ```ignore
43/// use osal_rs::os::{Queue, QueueFn};
44/// use osal_rs::utils::Error;
45/// 
46/// match Queue::new(10, 32) {
47///     Ok(queue) => { /* use queue */ },
48///     Err(Error::OutOfMemory) => println!("Failed to allocate queue"),
49///     Err(e) => println!("Other error: {:?}", e),
50/// }
51/// ```
52#[derive(Debug, Clone, PartialEq, Eq, Hash)]
53pub enum Error {
54    /// Insufficient memory to complete operation
55    OutOfMemory,
56    /// Queue send operation timed out
57    QueueSendTimeout,
58    /// Queue receive operation timed out
59    QueueReceiveTimeout,
60    /// Mutex operation timed out
61    MutexTimeout,
62    /// Failed to acquire mutex lock
63    MutexLockFailed,
64    /// Generic timeout error
65    Timeout,
66    /// Queue is full and cannot accept more items
67    QueueFull,
68    /// String conversion failed
69    StringConversionError,
70    /// Thread/task not found
71    TaskNotFound,
72    /// Invalid queue size specified
73    InvalidQueueSize,
74    /// Null pointer encountered
75    NullPtr,
76    /// Requested item not found
77    NotFound,
78    /// Index out of bounds
79    OutOfIndex,
80    /// Invalid type for operation
81    InvalidType,
82    /// Unhandled error with description
83    Unhandled(&'static str)
84}
85
86/// CPU register size enumeration.
87///
88/// Identifies whether the target CPU uses 32-bit or 64-bit registers.
89/// This is used for platform-specific tick count overflow handling.
90#[derive(PartialEq, Eq, Clone, Copy, Debug)]
91pub enum CpuRegisterSize {
92    /// 64-bit CPU registers
93    Bit64,
94    /// 32-bit CPU registers
95    Bit32
96}
97
98/// Boolean type compatible with RTOS return values.
99///
100/// Many RTOS functions return 0 for success and non-zero for failure.
101/// This type provides a Rust-idiomatic way to work with such values.
102///
103/// # Examples
104///
105/// ```ignore
106/// use osal_rs::os::{Semaphore, SemaphoreFn};
107/// use osal_rs::utils::OsalRsBool;
108/// use core::time::Duration;
109/// 
110/// let sem = Semaphore::new(1, 1).unwrap();
111/// 
112/// match sem.wait(Duration::from_millis(100)) {
113///     OsalRsBool::True => println!("Acquired semaphore"),
114///     OsalRsBool::False => println!("Failed to acquire"),
115/// }
116/// 
117/// // Can also convert to bool
118/// if sem.signal().into() {
119///     println!("Semaphore signaled");
120/// }
121/// ```
122#[derive(PartialEq, Eq, Clone, Copy, Debug)]
123#[repr(u8)]
124pub enum OsalRsBool {
125    /// Operation failed or condition is false
126    False = 1,
127    /// Operation succeeded or condition is true
128    True = 0
129}
130
131/// Maximum delay constant for blocking operations.
132///
133/// When used as a timeout parameter, indicates the operation should
134/// block indefinitely until it succeeds.
135///
136/// # Examples
137///
138/// ```ignore
139/// use osal_rs::os::{Mutex, MutexFn};
140/// use osal_rs::utils::MAX_DELAY;
141/// 
142/// let mutex = Mutex::new(0);
143/// let guard = mutex.lock();  // Blocks forever if needed
144/// ```
145pub const MAX_DELAY: Duration = Duration::from_millis(usize::MAX as u64);
146
147/// Standard Result type for OSAL-RS operations.
148///
149/// Uses [`Error`] as the default error type.
150pub type Result<T, E = Error> = core::result::Result<T, E>;
151
152/// Pointer to pointer type for C FFI.
153pub type DoublePtr = *mut *mut c_void;
154
155/// Mutable pointer type for C FFI.
156pub type Ptr = *mut c_void;
157
158/// Const pointer type for C FFI.
159pub type ConstPtr = *const c_void;
160
161/// Shortcut for Arc<Mutex<T>>
162pub type ArcMux<T> = Arc<Mutex<T>>;
163
164/// Determines the CPU register size at compile time.
165///
166/// This constant function checks the size of `usize` to determine whether
167/// the target architecture uses 32-bit or 64-bit registers. This information
168/// is used for platform-specific optimizations and overflow handling.
169///
170/// # Returns
171///
172/// * [`CpuRegisterSize::Bit64`] - For 64-bit architectures
173/// * [`CpuRegisterSize::Bit32`] - For 32-bit architectures
174///
175/// # Examples
176///
177/// ```ignore
178/// use osal_rs::utils::{register_bit_size, CpuRegisterSize};
179/// 
180/// match register_bit_size() {
181///     CpuRegisterSize::Bit64 => println!("Running on 64-bit platform"),
182///     CpuRegisterSize::Bit32 => println!("Running on 32-bit platform"),
183/// }
184/// ```
185pub const fn register_bit_size() -> CpuRegisterSize {
186    if size_of::<usize>() == 8 {
187        CpuRegisterSize::Bit64
188    } else {
189        CpuRegisterSize::Bit32
190    }
191}
192
193/// Converts a C string pointer to a Rust String.
194///
195/// This macro safely converts a raw C string pointer (`*const c_char`) into
196/// a Rust `String`. It handles UTF-8 conversion gracefully using lossy conversion.
197///
198/// # Safety
199///
200/// The pointer must be valid and point to a null-terminated C string.
201///
202/// # Examples
203///
204/// ```ignore
205/// use osal_rs::from_c_str;
206/// use core::ffi::c_char;
207/// 
208/// extern "C" {
209///     fn get_system_name() -> *const c_char;
210/// }
211/// 
212/// let name = from_c_str!(get_system_name());
213/// println!("System: {}", name);
214/// ```
215#[macro_export]
216macro_rules! from_c_str {
217    ($str:expr) => {
218        unsafe {
219            let c_str = core::ffi::CStr::from_ptr($str);
220            alloc::string::String::from_utf8_lossy(c_str.to_bytes()).to_string()
221        }
222    };
223}
224
225/// Converts a Rust string to a CString with error handling.
226///
227/// This macro creates a `CString` from a Rust string reference, returning
228/// a `Result` that can be used with the `?` operator. If the conversion fails
229/// (e.g., due to interior null bytes), it returns an appropriate error.
230///
231/// # Returns
232///
233/// * `Ok(CString)` - On successful conversion
234/// * `Err(Error::Unhandled)` - If the string contains interior null bytes
235///
236/// # Examples
237///
238/// ```ignore
239/// use osal_rs::to_cstring;
240/// use osal_rs::utils::Result;
241/// 
242/// fn pass_to_c_api(name: &str) -> Result<()> {
243///     let c_name = to_cstring!(name)?;
244///     // Use c_name.as_ptr() with C FFI
245///     Ok(())
246/// }
247/// ```
248#[macro_export]
249macro_rules! to_cstring {
250    ($s:expr) => {
251        alloc::ffi::CString::new($s.as_str())
252            .map_err(|_| $crate::utils::Error::Unhandled("Failed to convert string to CString"))
253    };
254}
255
256/// Converts a Rust string to a C string pointer.
257///
258/// This macro creates a `CString` from a Rust string and returns its raw pointer.
259/// **Warning**: This macro panics if the conversion fails. Consider using
260/// [`to_cstring!`] for safer error handling.
261///
262/// # Panics
263///
264/// Panics if the string contains interior null bytes.
265///
266/// # Examples
267///
268/// ```ignore
269/// use osal_rs::to_c_str;
270/// 
271/// extern "C" {
272///     fn set_name(name: *const core::ffi::c_char);
273/// }
274/// 
275/// let name = "FreeRTOS Task";
276/// unsafe {
277///     set_name(to_c_str!(name));
278/// }
279/// ```
280#[macro_export]
281macro_rules! to_c_str {
282    ($s:expr) => {
283        alloc::ffi::CString::new($s.as_ref() as &str).unwrap().as_ptr()
284    };
285}
286
287/// Converts a string to a fixed-size byte array.
288///
289/// This macro creates a byte array of the specified size and fills it with
290/// the bytes from the input string. If the string is shorter than the buffer,
291/// the remaining bytes are filled with spaces. If the string is longer, it
292/// is truncated to fit.
293///
294/// # Parameters
295///
296/// * `$str` - The source string to convert
297/// * `$buff_name` - The identifier name for the created buffer variable
298/// * `$buff_size` - The size of the byte array to create
299///
300/// # Examples
301///
302/// ```ignore
303/// use osal_rs::from_str_to_array;
304/// 
305/// let task_name = "MainTask";
306/// from_str_to_array!(task_name, name_buffer, 16);
307/// // name_buffer is now [u8; 16] containing "MainTask        "
308/// 
309/// // Use with C FFI
310/// extern "C" {
311///     fn create_task(name: *const u8, len: usize);
312/// }
313/// 
314/// unsafe {
315///     create_task(name_buffer.as_ptr(), name_buffer.len());
316/// }
317/// ```
318#[macro_export]
319macro_rules! from_str_to_array {
320    ($str:expr, $buff_name:ident, $buff_size:expr) => {
321        let mut $buff_name = [b' '; $buff_size];
322        let _bytes = $str.as_bytes();
323        let _len = core::cmp::min(_bytes.len(), $buff_size);
324        $buff_name[.._len].copy_from_slice(&_bytes[.._len]);
325    };
326}
327
328/// Extracts a typed parameter from an optional boxed Any reference.
329///
330/// This macro is used in thread/task entry points to safely extract and
331/// downcast parameters passed to the thread. It handles both the Option
332/// unwrapping and the type downcast, returning appropriate errors if either
333/// operation fails.
334///
335/// # Parameters
336///
337/// * `$param` - An `Option<Box<dyn Any>>` containing the parameter
338/// * `$t` - The type to downcast the parameter to
339///
340/// # Returns
341///
342/// * A reference to the downcasted value of type `$t`
343/// * `Err(Error::NullPtr)` - If the parameter is None
344/// * `Err(Error::InvalidType)` - If the downcast fails
345///
346/// # Examples
347///
348/// ```ignore
349/// use osal_rs::thread_extract_param;
350/// use osal_rs::utils::Result;
351/// use core::any::Any;
352/// 
353/// struct TaskConfig {
354///     priority: u8,
355///     stack_size: usize,
356/// }
357/// 
358/// fn task_entry(param: Option<Box<dyn Any>>) -> Result<()> {
359///     let config = thread_extract_param!(param, TaskConfig);
360///     
361///     println!("Priority: {}", config.priority);
362///     println!("Stack: {}", config.stack_size);
363///     
364///     Ok(())
365/// }
366/// ```
367#[macro_export]
368macro_rules! thread_extract_param {
369    ($param:expr, $t:ty) => {
370        match $param.as_ref() {
371            Some(p) => {
372                match p.downcast_ref::<$t>() {
373                    Some(value) => value,
374                    None => return Err($crate::utils::Error::InvalidType),
375                }
376            }
377            None => return Err($crate::utils::Error::NullPtr),
378        }
379    };
380}
381
382/// Creates an Arc<Mutex<T>> from a value.
383///
384/// This is a convenience macro to reduce boilerplate when creating
385/// thread-safe shared data structures.
386///
387/// # Examples
388///
389/// ```ignore
390/// use osal_rs::arcmux;
391/// 
392/// let shared_counter = arcmux!(0);
393/// // Equivalent to: Arc::new(Mutex::new(0))
394/// ```
395#[macro_export]
396macro_rules! arcmux {
397    ($value:expr) => {
398        alloc::sync::Arc::new($crate::os::MutexFn::new($value))
399    };
400}
401
402
403
404/// Fixed-size byte array wrapper with string conversion utilities.
405///
406/// `Bytes` is a generic wrapper around a fixed-size byte array that provides
407/// convenient methods for converting between strings and byte arrays. It's
408/// particularly useful for interfacing with C APIs that expect fixed-size
409/// character buffers, or for storing strings in embedded systems with
410/// constrained memory.
411///
412/// # Type Parameters
413///
414/// * `SIZE` - The size of the internal byte array (default: 0)
415///
416/// # Examples
417///
418/// ```ignore
419/// use osal_rs::utils::Bytes;
420/// 
421/// // Create an empty 32-byte buffer
422/// let mut buffer = Bytes::<32>::new();
423/// 
424/// // Create a buffer from a string
425/// let name = Bytes::<16>::new_by_str("TaskName");
426/// println!("{}", name); // Prints "TaskName"
427/// 
428/// // Create from any type that implements ToString
429/// let number = 42;
430/// let num_bytes = Bytes::<8>::new_by_string(&number);
431/// ```
432#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
433pub struct Bytes<const SIZE: usize = 0> (pub [u8; SIZE]);
434
435impl<const SIZE: usize> Deref for Bytes<SIZE> {
436    type Target = [u8; SIZE];
437
438    /// Dereferences to the underlying byte array.
439    ///
440    /// This allows `Bytes` to be used anywhere a `[u8; SIZE]` reference is expected.
441    ///
442    /// # Examples
443    ///
444    /// ```ignore
445    /// use osal_rs::utils::Bytes;
446    /// 
447    /// let bytes = Bytes::<8>::new_by_str("test");
448    /// assert_eq!(bytes[0], b't');
449    /// ```
450    fn deref(&self) -> &Self::Target {
451        &self.0
452    }
453}
454
455impl<const SIZE: usize> Display for Bytes<SIZE> {
456    /// Formats the byte array as a C-style null-terminated string.
457    ///
458    /// This implementation treats the byte array as a C string and converts it
459    /// to a Rust string for display. If the conversion fails, it displays
460    /// "Conversion error".
461    ///
462    /// # Safety
463    ///
464    /// This method assumes the byte array contains valid UTF-8 data and is
465    /// null-terminated. Invalid data may result in the error message being displayed.
466    ///
467    /// # Examples
468    ///
469    /// ```ignore
470    /// use osal_rs::utils::Bytes;
471    /// 
472    /// let bytes = Bytes::<16>::new_by_str("Hello");
473    /// println!("{}", bytes); // Prints "Hello"
474    /// ```
475    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
476        let str = unsafe {
477            CStr::from_ptr(self.0.as_ptr() as *const c_char)
478            .to_str()
479            .unwrap_or("Conversion error")
480        };
481        
482        write!(f, "{}", str.to_string())
483    }
484}
485
486impl AsSyncStr for Bytes<> {
487    /// Returns a string slice reference.
488    ///
489    /// This method provides access to the underlying string data in a way
490    /// that is safe to use across thread boundaries.
491    ///
492    /// # Returns
493    ///
494    /// A reference to a string slice with lifetime tied to `self`.
495    fn as_str(&self) -> &str {
496        unsafe {
497            CStr::from_ptr(self.0.as_ptr() as *const c_char)
498            .to_str()
499            .unwrap_or("Conversion error")
500        }
501    }
502}
503
504impl<const SIZE: usize> Bytes<SIZE> {
505    /// Creates a new `Bytes` instance filled with zeros.
506    ///
507    /// This is a const function, allowing it to be used in const contexts
508    /// and static variable declarations.
509    ///
510    /// # Returns
511    ///
512    /// A `Bytes` instance with all bytes set to 0.
513    ///
514    /// # Examples
515    ///
516    /// ```ignore
517    /// use osal_rs::utils::Bytes;
518    /// 
519    /// const BUFFER: Bytes<64> = Bytes::new();
520    /// 
521    /// let runtime_buffer = Bytes::<32>::new();
522    /// assert_eq!(runtime_buffer[0], 0);
523    /// ```
524    pub const fn new() -> Self {
525        Self( [0u8; SIZE] )
526    }
527
528    /// Creates a new `Bytes` instance from a string slice.
529    ///
530    /// Copies the bytes from the input string into the fixed-size array.
531    /// If the string is shorter than `SIZE`, the remaining bytes are zero-filled.
532    /// If the string is longer, it is truncated to fit.
533    ///
534    /// # Parameters
535    ///
536    /// * `str` - The source string to convert
537    ///
538    /// # Returns
539    ///
540    /// A `Bytes` instance containing the string data.
541    ///
542    /// # Examples
543    ///
544    /// ```ignore
545    /// use osal_rs::utils::Bytes;
546    /// 
547    /// let short = Bytes::<16>::new_by_str("Hi");
548    /// // Internal array: [b'H', b'i', 0, 0, 0, ...]
549    /// 
550    /// let exact = Bytes::<5>::new_by_str("Hello");
551    /// // Internal array: [b'H', b'e', b'l', b'l', b'o']
552    /// 
553    /// let long = Bytes::<3>::new_by_str("Hello");
554    /// // Internal array: [b'H', b'e', b'l'] (truncated)
555    /// ```
556    pub fn new_by_str(str: &str) -> Self {
557
558        let mut array = [0u8; SIZE];
559        
560        let mut i = 0usize ;
561        for byte in str.as_bytes() {
562            if i > SIZE - 1{
563                break;
564            }
565            array[i] = *byte;
566            i += 1;
567        }  
568
569        Self( array )
570    }
571
572    /// Creates a new `Bytes` instance from any type implementing `ToString`.
573    ///
574    /// This is a convenience wrapper around [`new_by_str`](Self::new_by_str)
575    /// that first converts the input to a string.
576    ///
577    /// # Parameters
578    ///
579    /// * `str` - Any value that implements `ToString`
580    ///
581    /// # Returns
582    ///
583    /// A `Bytes` instance containing the string representation of the input.
584    ///
585    /// # Examples
586    ///
587    /// ```ignore
588    /// use osal_rs::utils::Bytes;
589    /// 
590    /// // From integer
591    /// let num_bytes = Bytes::<8>::new_by_string(&42);
592    /// 
593    /// // From String
594    /// let string = String::from("Task");
595    /// let str_bytes = Bytes::<16>::new_by_string(&string);
596    /// 
597    /// // From custom type with ToString
598    /// #[derive(Debug)]
599    /// struct TaskId(u32);
600    /// impl ToString for TaskId {
601    ///     fn to_string(&self) -> String {
602    ///         format!("Task-{}", self.0)
603    ///     }
604    /// }
605    /// let task_bytes = Bytes::<16>::new_by_string(&TaskId(5));
606    /// ```
607    pub fn new_by_string(str: &impl ToString) -> Self {
608        Self::new_by_str(&str.to_string())
609    }
610
611    /// Fills a mutable string slice with the contents of the byte array.
612    ///
613    /// Attempts to convert the internal byte array to a UTF-8 string and
614    /// copies it into the destination string slice. Only copies up to the
615    /// minimum of the source and destination lengths.
616    ///
617    /// # Parameters
618    ///
619    /// * `dest` - The destination string slice to fill
620    ///
621    /// # Panics
622    ///
623    /// Currently panics (todo!) if the byte array contains invalid UTF-8.
624    ///
625    /// # Examples
626    ///
627    /// ```ignore
628    /// use osal_rs::utils::Bytes;
629    /// 
630    /// let bytes = Bytes::<16>::new_by_str("Hello World");
631    /// 
632    /// let mut output = String::from("                "); // 16 spaces
633    /// bytes.fill_str(unsafe { output.as_mut_str() });
634    /// 
635    /// assert_eq!(&output[..11], "Hello World");
636    /// ```
637    pub fn fill_str(&mut self, dest: &mut str) {
638        match from_utf8_mut(&mut self.0) {
639            Ok(str) => {
640                let len = core::cmp::min(str.len(), dest.len());
641                unsafe {
642                    dest.as_bytes_mut()[..len].copy_from_slice(&str.as_bytes()[..len]);
643                }
644            }
645            Err(_) => todo!(),
646        }
647    }
648}
649
650/// Trait for types that can provide a string reference in a thread-safe manner.
651///
652/// This trait extends the basic string reference functionality with thread-safety
653/// guarantees by requiring both `Sync` and `Send` bounds. It's useful for types
654/// that need to provide string data across thread boundaries in a concurrent
655/// environment.
656///
657/// # Thread Safety
658///
659/// Implementors must be both `Sync` (safe to share references across threads) and
660/// `Send` (safe to transfer ownership across threads).
661///
662/// # Examples
663///
664/// ```ignore
665/// use osal_rs::utils::AsSyncStr;
666/// 
667/// struct ThreadSafeName {
668///     name: &'static str,
669/// }
670/// 
671/// impl AsSyncStr for ThreadSafeName {
672///     fn as_str(&self) -> &str {
673///         self.name
674///     }
675/// }
676/// 
677/// // Can be safely shared across threads
678/// fn use_in_thread(item: &dyn AsSyncStr) {
679///     println!("Name: {}", item.as_str());
680/// }
681/// ```
682pub trait AsSyncStr : Sync + Send { 
683    /// Returns a string slice reference.
684    ///
685    /// This method provides access to the underlying string data in a way
686    /// that is safe to use across thread boundaries.
687    ///
688    /// # Returns
689    ///
690    /// A reference to a string slice with lifetime tied to `self`.
691    fn as_str(&self) -> &str;
692}
693
694impl PartialEq for dyn AsSyncStr {
695    fn eq(&self, other: &Self) -> bool {
696        self.as_str() == other.as_str()
697    }
698}
699
700impl Eq for dyn AsSyncStr {}
701
702impl Debug for dyn AsSyncStr {
703    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
704        write!(f, "{}", self.as_str())
705    }
706}
707
708impl Display for dyn AsSyncStr {
709    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
710        write!(f, "{}", self.as_str())
711    }
712}
713