diff --git a/CHANGELOG.md b/CHANGELOG.md index 54b4c3d..b01a32f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,19 @@ * [#26] - Add `memchr` * [#27] - Add `qsort` +* [#28] - Add `strcat` and `strchr` +* [#29] - Clean up docs +* [#30] - Add padding in `sprintf` +* [#32] - Add `rand`, `srand` and `rand_r` +* [#34] - Add `strspn` and `strcspn` [#26]: https://github.com/rust-embedded-community/tinyrlibc/pull/26 [#27]: https://github.com/rust-embedded-community/tinyrlibc/pull/27 +[#28]: https://github.com/rust-embedded-community/tinyrlibc/pull/28 +[#29]: https://github.com/rust-embedded-community/tinyrlibc/pull/29 +[#30]: https://github.com/rust-embedded-community/tinyrlibc/pull/30 +[#32]: https://github.com/rust-embedded-community/tinyrlibc/pull/32 +[#34]: https://github.com/rust-embedded-community/tinyrlibc/pull/34 ## v0.4.0 (2024-03-22) diff --git a/Cargo.toml b/Cargo.toml index 62f6d78..d9e6692 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,8 @@ all = [ "strncmp", "strncpy", "strrchr", + "strspn", + "strcspn", "strstr", "strtoimax", "strtol", @@ -76,6 +78,8 @@ strncasecmp = [] strncmp = [] strncpy = [] strrchr = [] +strspn = [] +strcspn = [] strstr = [] strtoimax = [] strtol = [] diff --git a/src/lib.rs b/src/lib.rs index 6b7924e..fd656ef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,58 +11,85 @@ #![allow(clippy::missing_safety_doc)] #![allow(unused_imports)] +// Useful imports +mod ctype; +pub use self::ctype::*; + +// Stateless implementations. +// rustfmt will keep these in alphabetical order. +mod abs; +mod itoa; +mod memchr; +mod qsort; +mod rand_r; +mod snprintf; +mod strcat; +mod strchr; +mod strcmp; +mod strcpy; +mod strcspn; +mod strlen; +mod strncasecmp; +mod strncmp; +mod strncpy; +mod strrchr; +mod strspn; +mod strstr; +mod strtol; + +// Stateful implementations (which hence are optional). +// rustfmt will keep these in alphabetical order. #[cfg(feature = "alloc")] mod malloc; -#[cfg(feature = "alloc")] -pub use self::malloc::{calloc, free, malloc, realloc}; +#[cfg(feature = "rand")] +mod rand; +#[cfg(feature = "signal")] +mod signal; -mod itoa; +// Public re-exports. +// rustfmt will keep these in alphabetical order. +#[cfg(feature = "abs")] +pub use self::abs::abs; #[cfg(feature = "itoa")] pub use self::itoa::itoa; #[cfg(feature = "utoa")] pub use self::itoa::utoa; - -mod abs; -#[cfg(feature = "abs")] -pub use self::abs::abs; - -mod rand_r; -#[cfg(feature = "rand_r")] -pub use self::rand_r::{rand_r, RAND_MAX}; -#[cfg(feature = "rand")] -mod rand; +#[cfg(feature = "alloc")] +pub use self::malloc::{calloc, free, malloc, realloc}; +#[cfg(feature = "memchr")] +pub use self::memchr::memchr; +#[cfg(feature = "qsort")] +pub use self::qsort::qsort; #[cfg(feature = "rand")] pub use self::rand::{rand, srand}; - -mod strcmp; +#[cfg(feature = "rand_r")] +pub use self::rand_r::{rand_r, RAND_MAX}; +#[cfg(feature = "signal")] +pub use self::signal::{abort, raise, signal}; +#[cfg(feature = "strcat")] +pub use self::strcat::strcat; +#[cfg(feature = "strchr")] +pub use self::strchr::strchr; #[cfg(feature = "strcmp")] pub use self::strcmp::strcmp; - -mod strncmp; -#[cfg(feature = "strncmp")] -pub use self::strncmp::strncmp; - -mod strncasecmp; -#[cfg(feature = "strncasecmp")] -pub use self::strncasecmp::strncasecmp; - -mod strcpy; #[cfg(feature = "strcpy")] pub use self::strcpy::strcpy; - -mod strncpy; -#[cfg(feature = "strncpy")] -pub use self::strncpy::strncpy; - -mod strlen; +#[cfg(feature = "strcspn")] +pub use self::strcspn::strcspn; #[cfg(feature = "strlen")] pub use self::strlen::strlen; - -mod strcat; -#[cfg(feature = "strcat")] -pub use self::strcat::strcat; - -mod strtol; +#[cfg(feature = "strncasecmp")] +pub use self::strncasecmp::strncasecmp; +#[cfg(feature = "strncmp")] +pub use self::strncmp::strncmp; +#[cfg(feature = "strncpy")] +pub use self::strncpy::strncpy; +#[cfg(feature = "strrchr")] +pub use self::strrchr::strrchr; +#[cfg(feature = "strspn")] +pub use self::strspn::strspn; +#[cfg(feature = "strstr")] +pub use self::strstr::strstr; #[cfg(feature = "atoi")] pub use self::strtol::atoi; #[cfg(feature = "isalpha")] @@ -85,33 +112,3 @@ pub use self::strtol::strtoul; pub use self::strtol::strtoull; #[cfg(feature = "strtoumax")] pub use self::strtol::strtoumax; - -mod strstr; -#[cfg(feature = "strstr")] -pub use self::strstr::strstr; - -mod strchr; -#[cfg(feature = "strchr")] -pub use self::strchr::strchr; - -mod strrchr; -#[cfg(feature = "strrchr")] -pub use self::strrchr::strrchr; - -mod qsort; -#[cfg(feature = "qsort")] -pub use self::qsort::qsort; - -#[cfg(feature = "signal")] -mod signal; -#[cfg(feature = "signal")] -pub use self::signal::{abort, raise, signal}; - -mod memchr; -#[cfg(feature = "memchr")] -pub use self::memchr::memchr; - -mod snprintf; - -mod ctype; -pub use self::ctype::*; diff --git a/src/strcspn.rs b/src/strcspn.rs new file mode 100644 index 0000000..39a0cc6 --- /dev/null +++ b/src/strcspn.rs @@ -0,0 +1,94 @@ +//! Rust implementation of C library function `strcspn` +//! +//! Copyright (c) Ferrous Systems UK Ltd +//! Licensed under the Blue Oak Model Licence 1.0.0 + +use crate::{CChar, CInt}; + +/// Rust implementation of C library function `strcspn` +#[cfg_attr(feature = "strcspn", no_mangle)] +pub unsafe extern "C" fn strcspn(s: *const CChar, charset: *const CChar) -> usize { + if s.is_null() { + return 0; + } + if charset.is_null() { + return 0; + } + + let s = unsafe { core::ffi::CStr::from_ptr(s.cast()) }; + + let charset = unsafe { core::ffi::CStr::from_ptr(charset.cast()) }; + + let bytes = s.to_bytes(); + for (idx, b) in bytes.iter().enumerate() { + if is_c_in_charset(*b, charset) { + return idx; + } + } + + bytes.len() +} + +fn is_c_in_charset(c: u8, charset: &core::ffi::CStr) -> bool { + for b in charset.to_bytes() { + if c == *b { + return true; + } + } + false +} + +#[cfg(test)] +mod test { + #[test] + fn complete() { + let charset = c"0123456789"; + let s = c"abcdef"; + assert_eq!( + unsafe { super::strcspn(s.as_ptr().cast(), charset.as_ptr().cast()) }, + 6 + ); + } + + #[test] + fn subset() { + let charset = c"0123456789"; + let s = c"xyz1"; + assert_eq!( + unsafe { super::strcspn(s.as_ptr().cast(), charset.as_ptr().cast()) }, + 3 + ); + } + + #[test] + fn none() { + let charset = c"0123456789"; + let s = c"567"; + assert_eq!( + unsafe { super::strcspn(s.as_ptr().cast(), charset.as_ptr().cast()) }, + 0 + ); + } + + #[test] + fn empty_charset() { + let charset = c""; + let s = c"AABBCCDD"; + assert_eq!( + unsafe { super::strcspn(s.as_ptr().cast(), charset.as_ptr().cast()) }, + 8 + ); + } + + #[test] + fn empty_string() { + let charset = c"0123456789"; + let s = c""; + assert_eq!( + unsafe { super::strcspn(s.as_ptr().cast(), charset.as_ptr().cast()) }, + 0 + ); + } +} + +// End of file diff --git a/src/strspn.rs b/src/strspn.rs new file mode 100644 index 0000000..3a634ed --- /dev/null +++ b/src/strspn.rs @@ -0,0 +1,84 @@ +//! Rust implementation of C library function `strspn` +//! +//! Copyright (c) Ferrous Systems UK Ltd +//! Licensed under the Blue Oak Model Licence 1.0.0 + +use crate::{CChar, CInt}; + +/// Rust implementation of C library function `strspn` +#[cfg_attr(feature = "strspn", no_mangle)] +pub unsafe extern "C" fn strspn(s: *const CChar, charset: *const CChar) -> usize { + if s.is_null() { + return 0; + } + if charset.is_null() { + return 0; + } + + let s = unsafe { core::ffi::CStr::from_ptr(s.cast()) }; + + let charset = unsafe { core::ffi::CStr::from_ptr(charset.cast()) }; + + let bytes = s.to_bytes(); + for (idx, b) in bytes.iter().enumerate() { + if !is_c_in_charset(*b, charset) { + return idx; + } + } + + bytes.len() +} + +fn is_c_in_charset(c: u8, charset: &core::ffi::CStr) -> bool { + for b in charset.to_bytes() { + if c == *b { + return true; + } + } + false +} + +#[cfg(test)] +mod test { + #[test] + fn complete() { + let charset = c"0123456789"; + let s = c"987654321"; + assert_eq!( + unsafe { super::strspn(s.as_ptr().cast(), charset.as_ptr().cast()) }, + 9 + ); + } + + #[test] + fn subset() { + let charset = c"0123456789"; + let s = c"98xx7654321"; + assert_eq!( + unsafe { super::strspn(s.as_ptr().cast(), charset.as_ptr().cast()) }, + 2 + ); + } + + #[test] + fn empty_charset() { + let charset = c""; + let s = c"AABBCCDD"; + assert_eq!( + unsafe { super::strspn(s.as_ptr().cast(), charset.as_ptr().cast()) }, + 0 + ); + } + + #[test] + fn empty_string() { + let charset = c"0123456789"; + let s = c""; + assert_eq!( + unsafe { super::strspn(s.as_ptr().cast(), charset.as_ptr().cast()) }, + 0 + ); + } +} + +// End of file