From 2e69cf69758405fb7436aaf8d5f305024f3359c8 Mon Sep 17 00:00:00 2001 From: Myung Gyungmin Date: Tue, 16 Jan 2024 00:08:03 +0900 Subject: [PATCH 01/20] implemented nedded functions for real --- Cargo.toml | 24 +++++ src/atof.rs | 131 ++++++++++++++++++++++++++ src/atoi.rs | 104 +++++++++++++++++---- src/errno.rs | 154 +++++++++++++++++++++++++++++++ src/isdigit.rs | 31 +++++++ src/isspace.rs | 68 ++++++++++++++ src/lib.rs | 63 ++++++++++++- src/malloc.rs | 0 src/memchr.rs | 44 +++++++++ src/qsort.rs | 93 +++++++++++++++++++ src/qsort_r.rs | 88 ++++++++++++++++++ src/strtod.rs | 243 +++++++++++++++++++++++++++++++++++++++++++++++++ src/strtof.rs | 55 +++++++++++ 13 files changed, 1078 insertions(+), 20 deletions(-) create mode 100644 src/atof.rs create mode 100644 src/errno.rs create mode 100644 src/isdigit.rs create mode 100644 src/isspace.rs create mode 100644 src/malloc.rs create mode 100644 src/memchr.rs create mode 100644 src/qsort.rs create mode 100644 src/qsort_r.rs create mode 100644 src/strtod.rs create mode 100644 src/strtof.rs diff --git a/Cargo.toml b/Cargo.toml index 1dc9f50..6b74f57 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,8 @@ readme = "README.md" repository = "https://github.com/thejpster/tinyrlibc" [dependencies] +libm = "0.2" +critical-section = "1.1.*" [build-dependencies] cc = "1.0" @@ -29,6 +31,17 @@ all = [ "atoi", "itoa", "snprintf", + "qsort", + "qsort_r", + "atof", + "strtod", + "errno", + "atol", + "atoll", + "isspace", + "isdigit", + "strtof", + "memchr", ] abs = [] strcmp = [] @@ -43,3 +56,14 @@ strchr = [] atoi = [] itoa = [] snprintf = [] +qsort = [] +qsort_r = [] +atof = [] +strtod = [] +errno = [] +atol = [] +atoll = [] +isspace = [] +isdigit = [] +strtof = [] +memchr = [] diff --git a/src/atof.rs b/src/atof.rs new file mode 100644 index 0000000..97205ae --- /dev/null +++ b/src/atof.rs @@ -0,0 +1,131 @@ +//! Rust implementation of C library function `atof` +//! +//! Original code from the `c-ward` project. +//! Licensed under the MIT license. + +use crate::{CChar, CDouble, strtod}; + +#[no_mangle] +pub unsafe extern "C" fn atof(nptr: *const CChar) -> CDouble { + strtod(nptr, core::ptr::null_mut()) +} + +#[cfg(test)] +mod test { + #[test] + fn positive() { + let result = unsafe { super::atof(b"123.456\0".as_ptr()) }; + assert_eq!(result, 123.456); + } + + #[test] + fn negative() { + let result = unsafe { super::atof(b"-123.456\0".as_ptr()) }; + assert_eq!(result, -123.456); + } + + #[test] + fn zero() { + let result = unsafe { super::atof(b"0\0".as_ptr()) }; + assert_eq!(result, 0.0); + } + + #[test] + fn nan() { + let result = unsafe { super::atof(b"NaN\0".as_ptr()) }; + assert!(result.is_nan()); + } + + #[test] + fn inf() { + let result = unsafe { super::atof(b"Inf\0".as_ptr()) }; + assert!(result.is_infinite()); + } + + #[test] + fn neg_inf() { + let result = unsafe { super::atof(b"-Inf\0".as_ptr()) }; + assert!(result.is_infinite()); + assert!(result.is_sign_negative()); + } + + #[test] + fn empty() { + let result = unsafe { super::atof(b"\0".as_ptr()) }; + assert!(result == 0.0 ); + } + + #[test] + fn positive_scientific() { + let result = unsafe { super::atof(b"1.23456e2\0".as_ptr()) }; + assert_eq!(result, 123.456); + } + + #[test] + fn negative_scientific() { + let result = unsafe { super::atof(b"1.23456e-2\0".as_ptr()) }; + assert_eq!(result, 0.0123456); + } + + #[test] + fn positive_overflow() { + let result = unsafe { super::atof(b"1e10000\0".as_ptr()) }; + assert!(result.is_infinite()); + } + + #[test] + fn negative_overflow() { + let result = unsafe { super::atof(b"-1e10000\0".as_ptr()) }; + assert!(result.is_infinite()); + assert!(result.is_sign_negative()); + } + + #[test] + fn leading_whitespace() { + let result = unsafe { super::atof(b" \t\n\r123.456\0".as_ptr()) }; + assert_eq!(result, 123.456); + } + + #[test] + fn trailing_whitespace() { + let result = unsafe { super::atof(b"123.456 \t\n\r\0".as_ptr()) }; + assert_eq!(result, 123.456); + } + + #[test] + fn leading_plus() { + let result = unsafe { super::atof(b"+123.456\0".as_ptr()) }; + assert_eq!(result, 123.456); + } + + #[test] + fn leading_minus() { + let result = unsafe { super::atof(b"-123.456\0".as_ptr()) }; + assert_eq!(result, -123.456); + } + + #[test] + fn leading_plus_nan() { + let result = unsafe { super::atof(b"+NaN\0".as_ptr()) }; + assert!(result.is_nan()); + } + + #[test] + fn leading_minus_nan() { + let result = unsafe { super::atof(b"-NaN\0".as_ptr()) }; + assert!(result.is_nan()); + } + + #[test] + fn leading_plus_inf() { + let result = unsafe { super::atof(b"+Inf\0".as_ptr()) }; + assert!(result.is_infinite()); + } + + #[test] + fn leading_minus_inf() { + let result = unsafe { super::atof(b"-Inf\0".as_ptr()) }; + assert!(result.is_infinite()); + assert!(result.is_sign_negative()); + } +} diff --git a/src/atoi.rs b/src/atoi.rs index 56642f5..d8c2e1e 100644 --- a/src/atoi.rs +++ b/src/atoi.rs @@ -3,25 +3,91 @@ //! Copyright (c) Jonathan 'theJPster' Pallant 2019 //! Licensed under the Blue Oak Model Licence 1.0.0 -use crate::{strtol, CChar, CInt, CLong}; - -/// Converts a null-terminated string representing a decimal integer, into an -/// integer. No indication of error. -/// -/// ``` -/// use tinyrlibc::atoi; -/// assert_eq!(unsafe { atoi(b"123\0".as_ptr()) }, 123); -/// assert_eq!(unsafe { atoi(b"123x\0".as_ptr()) }, 123); -/// assert_eq!(unsafe { atoi(b"\0".as_ptr()) }, 0); -/// ``` +use crate::{CChar, CInt, CLong, CLongLong, isspace, isdigit}; + +use core::ops::{MulAssign, Neg, SubAssign}; + #[no_mangle] pub unsafe extern "C" fn atoi(s: *const CChar) -> CInt { - let result = strtol(s); - if result > CInt::max_value() as CLong { - CInt::max_value() - } else if result < CInt::min_value() as CLong { - CInt::min_value() - } else { - result as CInt - } + _atoi(s) +} + +#[no_mangle] +pub unsafe extern "C" fn atol(s: *const CChar) -> CLong { + _atoi(s) +} + +#[no_mangle] +pub unsafe extern "C" fn atoll(s: *const CChar) -> CLongLong { + _atoi(s) +} + +unsafe fn _atoi + From + Default>( + mut s: *const CChar, +) -> T { + let mut negate = false; + let mut n = T::default(); + + // Skip leading whitespace. + while isspace(*s as CInt) != 0 { + s = s.add(1); + } + + // Handle a sign. + match *s as u8 { + b'-' => { + negate = true; + s = s.add(1); + } + b'+' => { + s = s.add(1); + } + _ => {} + } + + // Handle digits. + while isdigit(*s as CInt) != 0 { + n *= T::from(10u8); + n -= (*s as u8 - b'0').into(); + s = s.add(1); + } + + if !negate { + n = -n; + } + + n +} + +#[cfg(test)] +mod test { + #[test] + fn positive() { + let result = unsafe { super::atoi(b"123\0".as_ptr()) }; + assert_eq!(result, 123); + } + + #[test] + fn negative() { + let result = unsafe { super::atoi(b"-123\0".as_ptr()) }; + assert_eq!(result, -123); + } + + #[test] + fn zero() { + let result = unsafe { super::atoi(b"0\0".as_ptr()) }; + assert_eq!(result, 0); + } + + #[test] + fn leading_whitespace() { + let result = unsafe { super::atoi(b" \t\n\r123\0".as_ptr()) }; + assert_eq!(result, 123); + } + + #[test] + fn trailing_whitespace() { + let result = unsafe { super::atoi(b"123 \t\n\r\0".as_ptr()) }; + assert_eq!(result, 123); + } } diff --git a/src/errno.rs b/src/errno.rs new file mode 100644 index 0000000..f272f1e --- /dev/null +++ b/src/errno.rs @@ -0,0 +1,154 @@ +//! Rust implementation of C library function `errno` +//! +//! Author: Gyungmin Myung +//! Licensed under the Blue Oak Model Licence 1.0.0 + +#[allow(non_camel_case_types)] + +use crate::{CInt, CVoid}; + +static mut _impure_ptr: *mut CVoid = core::ptr::null_mut(); + +pub struct Errno { + pub errno: CInt, +} + +pub fn errno(errno: CInt) -> Errno{ + Errno { errno } +} + +pub fn set_errno(errno: Errno) {} + +pub const EPERM: CInt = 1; +pub const ENOENT: CInt = 2; +pub const ESRCH: CInt = 3; +pub const EINTR: CInt = 4; +pub const EIO: CInt = 5; +pub const ENXIO: CInt = 6; +pub const E2BIG: CInt = 7; +pub const ENOEXEC: CInt = 8; +pub const EBADF: CInt = 9; +pub const ECHILD: CInt = 10; +pub const EAGAIN: CInt = 11; +pub const ENOMEM: CInt = 12; +pub const EACCES: CInt = 13; +pub const EFAULT: CInt = 14; +pub const ENOTBLK: CInt = 15; +pub const EBUSY: CInt = 16; +pub const EEXIST: CInt = 17; +pub const EXDEV: CInt = 18; +pub const ENODEV: CInt = 19; +pub const ENOTDIR: CInt = 20; +pub const EISDIR: CInt = 21; +pub const EINVAL: CInt = 22; +pub const ENFILE: CInt = 23; +pub const EMFILE: CInt = 24; +pub const ENOTTY: CInt = 25; +pub const ETXTBSY: CInt = 26; +pub const EFBIG: CInt = 27; +pub const ENOSPC: CInt = 28; +pub const ESPIPE: CInt = 29; +pub const EROFS: CInt = 30; +pub const EMLINK: CInt = 31; +pub const EPIPE: CInt = 32; +pub const EDOM: CInt = 33; +pub const ERANGE: CInt = 34; +pub const EDEADLK: CInt = 35; +pub const ENAMETOOLONG: CInt = 36; +pub const ENOLCK: CInt = 37; +pub const ENOSYS: CInt = 38; +pub const ENOTEMPTY: CInt = 39; +pub const ELOOP: CInt = 40; +pub const EWOULDBLOCK: CInt = EAGAIN; +pub const ENOMSG: CInt = 42; +pub const EIDRM: CInt = 43; +pub const ECHRNG: CInt = 44; +pub const EL2NSYNC: CInt = 45; +pub const EL3HLT: CInt = 46; +pub const EL3RST: CInt = 47; +pub const ELNRNG: CInt = 48; +pub const EUNATCH: CInt = 49; +pub const ENOCSI: CInt = 50; +pub const EL2HLT: CInt = 51; +pub const EBADE: CInt = 52; +pub const EBADR: CInt = 53; +pub const EXFULL: CInt = 54; +pub const ENOANO: CInt = 55; +pub const EBADRQC: CInt = 56; +pub const EBADSLT: CInt = 57; +pub const EDEADLOCK: CInt = EDEADLK; +pub const EBFONT: CInt = 59; +pub const ENOSTR: CInt = 60; +pub const ENODATA: CInt = 61; +pub const ETIME: CInt = 62; +pub const ENOSR: CInt = 63; +pub const ENONET: CInt = 64; +pub const ENOPKG: CInt = 65; +pub const EREMOTE: CInt = 66; +pub const ENOLINK: CInt = 67; +pub const EADV: CInt = 68; +pub const ESRMNT: CInt = 69; +pub const ECOMM: CInt = 70; +pub const EPROTO: CInt = 71; +pub const EMULTIHOP: CInt = 72; +pub const EDOTDOT: CInt = 73; +pub const EBADMSG: CInt = 74; +pub const EOVERFLOW: CInt = 75; +pub const ENOTUNIQ: CInt = 76; +pub const EBADFD: CInt = 77; +pub const EREMCHG: CInt = 78; +pub const ELIBACC: CInt = 79; +pub const ELIBBAD: CInt = 80; +pub const ELIBSCN: CInt = 81; +pub const ELIBMAX: CInt = 82; +pub const ELIBEXEC: CInt = 83; +pub const EILSEQ: CInt = 84; +pub const ERESTART: CInt = 85; +pub const ESTRPIPE: CInt = 86; +pub const EUSERS: CInt = 87; +pub const ENOTSOCK: CInt = 88; +pub const EDESTADDRREQ: CInt = 89; +pub const EMSGSIZE: CInt = 90; +pub const EPROTOTYPE: CInt = 91; +pub const ENOPROTOOPT: CInt = 92; +pub const EPROTONOSUPPORT: CInt = 93; +pub const ESOCKTNOSUPPORT: CInt = 94; +pub const EOPNOTSUPP: CInt = 95; +pub const ENOTSUP: CInt = EOPNOTSUPP; +pub const EPFNOSUPPORT: CInt = 96; +pub const EAFNOSUPPORT: CInt = 97; +pub const EADDRINUSE: CInt = 98; +pub const EADDRNOTAVAIL: CInt = 99; +pub const ENETDOWN: CInt = 100; +pub const ENETUNREACH: CInt = 101; +pub const ENETRESET: CInt = 102; +pub const ECONNABORTED: CInt = 103; +pub const ECONNRESET: CInt = 104; +pub const ENOBUFS: CInt = 105; +pub const EISCONN: CInt = 106; +pub const ENOTCONN: CInt = 107; +pub const ESHUTDOWN: CInt = 108; +pub const ETOOMANYREFS: CInt = 109; +pub const ETIMEDOUT: CInt = 110; +pub const ECONNREFUSED: CInt = 111; +pub const EHOSTDOWN: CInt = 112; +pub const EHOSTUNREACH: CInt = 113; +pub const EALREADY: CInt = 114; +pub const EINPROGRESS: CInt = 115; +pub const ESTALE: CInt = 116; +pub const EUCLEAN: CInt = 117; +pub const ENOTNAM: CInt = 118; +pub const ENAVAIL: CInt = 119; +pub const EISNAM: CInt = 120; +pub const EREMOTEIO: CInt = 121; +pub const EDQUOT: CInt = 122; +pub const ENOMEDIUM: CInt = 123; +pub const EMEDIUMTYPE: CInt = 124; +pub const ECANCELED: CInt = 125; +pub const ENOKEY: CInt = 126; +pub const EKEYEXPIRED: CInt = 127; +pub const EKEYREVOKED: CInt = 128; +pub const EKEYREJECTED: CInt = 129; +pub const EOWNERDEAD: CInt = 130; +pub const ENOTRECOVERABLE: CInt = 131; +pub const ERFKILL: CInt = 132; diff --git a/src/isdigit.rs b/src/isdigit.rs new file mode 100644 index 0000000..f42b73d --- /dev/null +++ b/src/isdigit.rs @@ -0,0 +1,31 @@ +//! Rust implementation of C library function `isspace` +//! +//! Author: Gyungmin Myung +//! Licensed under the MIT license. + +use crate::CInt; + +#[no_mangle] +pub extern "C" fn isdigit(c: CInt) -> CInt { + CInt::from(c >= CInt::from(b'0') && c <= CInt::from(b'9')) +} + +#[cfg(test)] +mod test { + use crate::CInt; + #[test] + fn all_digits() { + for i in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'] { + let result = super::isdigit(i as CInt); + assert_eq!(result, 1); + } + } + + #[test] + fn non_digits() { + for i in ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'] { + let result = super::isdigit(i as CInt); + assert_eq!(result, 0); + } + } +} diff --git a/src/isspace.rs b/src/isspace.rs new file mode 100644 index 0000000..947a319 --- /dev/null +++ b/src/isspace.rs @@ -0,0 +1,68 @@ +//! Rust implementation of C library function `isspace` +//! +//! Author: Gyungmin Myung +//! Licensed under the MIT license. + +use crate::{CInt, CChar}; +#[no_mangle] +pub extern "C" fn isspace(c: CInt) -> CInt { + CInt::from( + c == CInt::from(b' ') + || c == CInt::from(b'\t') + || c == CInt::from(b'\n') + || c == CInt::from(b'\r') + || c == 0x0b + || c == 0x0c, + ) +} + +#[cfg(test)] +mod test { + #[test] + fn space() { + let result = super::isspace(' ' as i32); + assert_eq!(result, 1); + } + + #[test] + fn tab() { + let result = super::isspace('\t' as i32); + assert_eq!(result, 1); + } + + #[test] + fn newline() { + let result = super::isspace('\n' as i32); + assert_eq!(result, 1); + } + + #[test] + fn carriage_return() { + let result = super::isspace('\r' as i32); + assert_eq!(result, 1); + } + + #[test] + fn form_feed() { + let result = super::isspace('\x0c' as i32); + assert_eq!(result, 1); + } + + #[test] + fn vertical_tab() { + let result = super::isspace('\x0b' as i32); + assert_eq!(result, 1); + } + + #[test] + fn zero() { + let result = super::isspace('0' as i32); + assert_eq!(result, 0); + } + + #[test] + fn a() { + let result = super::isspace('a' as i32); + assert_eq!(result, 0); + } +} diff --git a/src/lib.rs b/src/lib.rs index ef74727..a21d14d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -63,10 +63,14 @@ mod strchr; #[cfg(feature = "strchr")] pub use self::strchr::strchr; -#[cfg(feature = "atoi")] +#[cfg(any(feature = "atoi", feature = "atol", feature = "atoll"))] mod atoi; #[cfg(feature = "atoi")] pub use self::atoi::atoi; +#[cfg(feature = "atol")] +pub use self::atoi::atol; +#[cfg(feature = "atoll")] +pub use self::atoi::atoll; #[cfg(feature = "itoa")] mod itoa; @@ -76,6 +80,63 @@ pub use self::itoa::itoa; #[cfg(feature = "snprintf")] mod snprintf; +#[cfg(feature = "qsort")] +mod qsort; +#[cfg(feature = "qsort")] +pub use self::qsort::qsort; + +#[cfg(feature = "qsort_r")] +mod qsort_r; +#[cfg(feature = "qsort_r")] +pub use self::qsort_r::qsort_r; + +#[cfg(feature = "atof")] +mod atof; +#[cfg(feature = "atof")] +pub use self::atof::atof; + +#[cfg(feature = "strtod")] +mod strtod; +#[cfg(feature = "strtod")] +pub use self::strtod::strtod; + +#[cfg(feature = "strtof")] +mod strtof; +#[cfg(feature = "strtof")] +pub use self::strtof::strtof; + +#[cfg(feature = "isspace")] +mod isspace; +#[cfg(feature = "isspace")] +pub use self::isspace::isspace; + +#[cfg(feature = "isdigit")] +mod isdigit; +#[cfg(feature = "isdigit")] +pub use self::isdigit::isdigit; + +#[cfg(feature = "errno")] +mod errno; +#[cfg(feature = "errno")] +pub use self::errno::{errno, set_errno}; + +#[cfg(feature = "memchr")] +mod memchr; +#[cfg(feature = "memchr")] +pub use self::memchr::memchr; + +/// `void` +pub type CVoid = ::core::ffi::c_void; + +/// `size_t` +pub type CSizeT = usize; + +/// `double` +pub type CDouble = core::ffi::c_double; + +/// `float` +pub type CFloat = core::ffi::c_float; + /// `long long int` pub type CLongLong = ::core::ffi::c_longlong; diff --git a/src/malloc.rs b/src/malloc.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/memchr.rs b/src/memchr.rs new file mode 100644 index 0000000..e4d3272 --- /dev/null +++ b/src/memchr.rs @@ -0,0 +1,44 @@ +//! Rust implementation of C library function `memchr` +//! +//! Author: Gyungmin Myung +//! Licensed under the MIT license. + +use crate::{CInt, CVoid, CSizeT}; + +#[no_mangle] +pub unsafe extern "C" fn memchr( + s: *const CVoid, + c: CInt, + n: CSizeT, +) -> *mut CVoid { + core::slice::from_raw_parts(s.cast::(), n as usize) + .iter() + .position(|&x| x == c as u8) + .map_or(core::ptr::null_mut(), |x| s.add(x) as _) +} + +#[cfg(test)] +mod test { + use crate::{CInt, CSizeT}; + + #[test] + fn find_char() { + let s = b"hello world\0"; + let result = unsafe { super::memchr(s.as_ptr().cast(), 'w' as CInt, 12 as CSizeT) }; + assert_eq!(result as *const _, s.as_ptr().wrapping_add(6)); + } + + #[test] + fn find_null() { + let s = b"hello world\0"; + let result = unsafe { super::memchr(s.as_ptr().cast(), 0, 12 as CSizeT) }; + assert_eq!(result as *const _, s.as_ptr().wrapping_add(11)); + } + + #[test] + fn find_nothing() { + let s = b"hello world\0"; + let result = unsafe { super::memchr(s.as_ptr().cast(), 'z' as CInt, 12 as CSizeT) }; + assert_eq!(result, core::ptr::null_mut()); + } +} diff --git a/src/qsort.rs b/src/qsort.rs new file mode 100644 index 0000000..0c199b4 --- /dev/null +++ b/src/qsort.rs @@ -0,0 +1,93 @@ +//! Rust implementation of C library function `qsort` +//! +//! Original code from the `c-ward` project. +//! Licensed under the MIT license. + +use crate::{CVoid, CInt, CSizeT}; + +#[no_mangle] +pub unsafe extern "C" fn qsort( + base: *mut CVoid, + nmemb: CSizeT, + width: CSizeT, + compar: Option CInt>, +) { + + let compar = compar.unwrap(); + + if nmemb <= 1 { + return; + } + + let base = base.cast::(); + let mut gap = nmemb; + + loop { + gap = next_gap(gap); + + let mut any_swapped = false; + let mut a = base; + let mut b = base.add(gap * width); + for _ in 0..nmemb - gap { + if compar(a.cast(), b.cast()) > 0 { + swap(a, b, width); + any_swapped = true; + } + a = a.add(width); + b = b.add(width); + } + + if gap <= 1 && !any_swapped { + break; + } + } +} + +fn next_gap(gap: CSizeT) -> CSizeT { + let gap = (gap * 10) / 13; + + if gap == 9 || gap == 10 { + 11 // apply the "rule of 11" + } else if gap <= 1 { + 1 + } else { + gap + } +} + +unsafe fn swap(a: *mut u8, b: *mut u8, width: CSizeT) { + for i in 0..width { + core::ptr::swap(a.add(i), b.add(i)); + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_qsort() { + let mut arr = [5, 4, 3, 2, 1]; + unsafe { + qsort( + arr.as_mut_ptr().cast(), + arr.len(), + core::mem::size_of::(), + Some(cmp), + ); + } + assert_eq!(arr, [1, 2, 3, 4, 5]); + } + + unsafe extern "C" fn cmp(a: *const CVoid, b: *const CVoid) -> CInt { + let a = a.cast::(); + let b = b.cast::(); + if *a < *b { + -1 + } else if *a > *b { + 1 + } else { + 0 + } + } +} diff --git a/src/qsort_r.rs b/src/qsort_r.rs new file mode 100644 index 0000000..7096d4b --- /dev/null +++ b/src/qsort_r.rs @@ -0,0 +1,88 @@ +//! Rust implementation of C library function `qsort_r` +//! +//! Original code from the `c-ward` project. +//! Licensed under the MIT license. + +use crate::{CVoid, CInt, CSizeT}; + +#[no_mangle] +pub unsafe extern "C" fn qsort_r( + base: *mut CVoid, + nmemb: CSizeT, + width: CSizeT, + compar: Option CInt>, + arg: *mut CVoid, +) { + let compar = compar.unwrap(); + + if nmemb <= 1 { + return; + } + + let base = base.cast::(); + let mut gap = nmemb; + + loop { + gap = next_gap(gap); + + let mut any_swapped = false; + let mut a = base; + let mut b = base.add(gap * width); + for _ in 0..nmemb - gap { + if compar(a.cast(), b.cast(), arg) > 0 { + swap(a, b, width); + any_swapped = true; + } + a = a.add(width); + b = b.add(width); + } + + if gap <= 1 && !any_swapped { + break; + } + } +} + +fn next_gap(gap: CSizeT) -> CSizeT { + let gap = (gap * 10) / 13; + + if gap == 9 || gap == 10 { + 11 // apply the "rule of 11" + } else if gap <= 1 { + 1 + } else { + gap + } +} + +unsafe fn swap(a: *mut u8, b: *mut u8, width: CSizeT) { + for i in 0..width { + core::ptr::swap(a.add(i), b.add(i)); + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_qsort() { + let mut data = [5, 4, 3, 2, 1]; + unsafe { + qsort_r( + data.as_mut_ptr().cast(), + data.len(), + core::mem::size_of::(), + Some(cmp_r), + core::ptr::null_mut(), + ); + } + assert_eq!(data, [1, 2, 3, 4, 5]); + } + + unsafe extern "C" fn cmp_r(a: *const CVoid, b: *const CVoid, _: *mut CVoid) -> CInt { + let a = a.cast::(); + let b = b.cast::(); + (*a).cmp(&*b) as CInt + } +} diff --git a/src/strtod.rs b/src/strtod.rs new file mode 100644 index 0000000..6621d64 --- /dev/null +++ b/src/strtod.rs @@ -0,0 +1,243 @@ +//! Rust implementation of C library function `strtod` +//! +//! Original code from the `c-ward` project. +//! Licensed under the MIT license. + +use crate::{CChar, CFloat, CDouble, errno::*}; +use core::str::FromStr; +use core::{slice, str}; + +#[no_mangle] +pub unsafe extern "C" fn strtod(nptr: *const CChar, endptr: *mut *mut CChar) -> CDouble { + let nptr = nptr.cast::(); + let orig = nptr; + let (nptr, format) = scan_float(nptr); + let s = make_str(orig, nptr); + + match format { + Format::Hexadecimal(_any_nonzero) => { + todo!("hexadecimal float parsing") + } + Format::Decimal(any_nonzero) => { + if let Ok(f) = f64::from_str(s) { + set_endptr(endptr, nptr); + set_errno_f64(f, any_nonzero); + return f; + } + } + Format::Infinity => { + set_endptr(endptr, nptr); + return if s.starts_with('-') { + -f64::INFINITY + } else { + f64::INFINITY + }; + } + Format::NaN(payload) => { + let result = if s.starts_with('-') { + libm::copysign(f64::NAN, -1.0) + } else { + libm::copysign(f64::NAN, 1.0) + }; + if let Some(payload) = payload { + if (libm::copysign(result, -1.0).to_bits() & payload) == 0 { + set_endptr(endptr, nptr); + return f64::from_bits(result.to_bits() | payload); + } + } else { + set_endptr(endptr, nptr); + return result; + } + } + } + + set_endptr(endptr, orig); + 0.0 +} + +pub unsafe fn make_str<'a>(start: *const u8, nptr: *const u8) -> &'a str { + str::from_utf8_unchecked(slice::from_raw_parts( + start, + nptr.offset_from(start) as usize, + )) + .trim_start() +} + +pub unsafe fn set_endptr(endptr: *mut *mut CChar, nptr: *const u8) { + if !endptr.is_null() { + *endptr = nptr.cast_mut().cast(); + } +} + +// If we tried to parse a number but got infinity, or if the number was +// subnormal, or we saw non-zero digits but got zero, set errno. +pub fn set_errno_f32(f: f32, any_nonzero: bool) { + if f.is_infinite() || f.is_subnormal() || (f == 0.0 && any_nonzero) { + set_errno(errno(ERANGE)); + } +} + +fn set_errno_f64(f: f64, any_nonzero: bool) { + if f.is_infinite() || f.is_subnormal() || (f == 0.0 && any_nonzero) { + set_errno(errno(ERANGE)); + } +} + +pub unsafe fn scan_float(mut nptr: *const u8) -> (*const u8, Format) { + while (*nptr).is_ascii_whitespace() { + nptr = nptr.add(1); + } + + if *nptr == b'-' || *nptr == b'+' { + nptr = nptr.add(1); + } + + if (*nptr).to_ascii_lowercase() == b'i' + && (*nptr.add(1)).to_ascii_lowercase() == b'n' + && (*nptr.add(2)).to_ascii_lowercase() == b'f' + { + nptr = nptr.add(3); + if (*nptr).to_ascii_lowercase() == b'i' + && (*nptr.add(1)).to_ascii_lowercase() == b'n' + && (*nptr.add(2)).to_ascii_lowercase() == b'i' + && (*nptr.add(3)).to_ascii_lowercase() == b't' + && (*nptr.add(4)).to_ascii_lowercase() == b'y' + { + nptr = nptr.add(5); + } + return (nptr, Format::Infinity); + } + + if (*nptr).to_ascii_lowercase() == b'n' + && (*nptr.add(1)).to_ascii_lowercase() == b'a' + && (*nptr.add(2)).to_ascii_lowercase() == b'n' + { + nptr = nptr.add(3); + if *nptr == b'(' { + let paren = nptr; + nptr = nptr.add(1); + let payload = if *nptr == b'0' && (*nptr.add(1)).to_ascii_lowercase() == b'x' { + nptr = nptr.add(2); + let start = nptr; + while (*nptr).is_ascii_hexdigit() { + nptr = nptr.add(1); + } + let s = make_str(start, nptr); + if s.is_empty() { + 0 + } else { + u64::from_str_radix(s, 16).unwrap() + } + } else { + let start = nptr; + while (*nptr).is_ascii_digit() { + nptr = nptr.add(1); + } + let s = make_str(start, nptr); + if s.is_empty() { + 0 + } else { + s.parse().unwrap() + } + }; + if *nptr == b')' { + nptr = nptr.add(1); + return (nptr, Format::NaN(Some(payload))); + } + nptr = paren; + } + return (nptr, Format::NaN(None)); + } + + let mut hex = false; + if *nptr == b'0' { + nptr = nptr.add(1); + if *nptr == b'x' || *nptr == b'X' { + nptr = nptr.add(1); + hex = true; + } + } + + let mut any_nonzero = false; + if hex { + while (*nptr).is_ascii_hexdigit() { + if *nptr != b'0' { + any_nonzero = true; + } + nptr = nptr.add(1); + } + } else { + while (*nptr).is_ascii_digit() { + if *nptr != b'0' { + any_nonzero = true; + } + nptr = nptr.add(1); + } + } + + if *nptr == b'.' { + nptr = nptr.add(1); + + if hex { + while (*nptr).is_ascii_hexdigit() { + if *nptr != b'0' { + any_nonzero = true; + } + nptr = nptr.add(1); + } + } else { + while (*nptr).is_ascii_digit() { + if *nptr != b'0' { + any_nonzero = true; + } + nptr = nptr.add(1); + } + } + } + + let mut before_exp = None; + if hex { + if *nptr == b'p' || *nptr == b'P' { + before_exp = Some(nptr); + nptr = nptr.add(1); + } + } else { + if *nptr == b'e' || *nptr == b'E' { + before_exp = Some(nptr); + nptr = nptr.add(1); + } + } + + if let Some(before_exp) = before_exp { + if *nptr == b'-' || *nptr == b'+' { + nptr = nptr.add(1); + } + + if (*nptr).is_ascii_digit() { + while (*nptr).is_ascii_digit() { + nptr = nptr.add(1); + } + } else { + nptr = before_exp; + } + } + + ( + nptr, + if hex { + if before_exp.is_none() { + todo!("strtod hexadecimal format with no `p`"); + } + Format::Hexadecimal(any_nonzero) + } else { + Format::Decimal(any_nonzero) + }, + ) +} + +pub enum Format { + Decimal(bool), + Hexadecimal(bool), + Infinity, + NaN(Option), +} diff --git a/src/strtof.rs b/src/strtof.rs new file mode 100644 index 0000000..1620997 --- /dev/null +++ b/src/strtof.rs @@ -0,0 +1,55 @@ + +use crate::{CChar, CFloat, CDouble, errno::*, strtod::*}; +use core::str::FromStr; +use core::{slice, str}; + +#[no_mangle] +pub unsafe extern "C" fn strtof(nptr: *const CChar, endptr: *mut *mut CChar) -> CFloat { + + let nptr = nptr.cast::(); + let orig = nptr; + let (nptr, format) = scan_float(nptr); + let s = make_str(orig, nptr); + + match format { + Format::Hexadecimal(_any_nonzero) => { + todo!("hexadecimal float parsing") + } + Format::Decimal(any_nonzero) => { + if let Ok(f) = f32::from_str(s) { + set_endptr(endptr, nptr); + set_errno_f32(f, any_nonzero); + return f; + } + } + Format::Infinity => { + set_endptr(endptr, nptr); + return if s.starts_with('-') { + -f32::INFINITY + } else { + f32::INFINITY + }; + } + Format::NaN(payload) => { + let result = if s.starts_with('-') { + libm::copysignf(f32::NAN, -1.0) + } else { + libm::copysignf(f32::NAN, 1.0) + }; + if let Some(payload) = payload { + if let Ok(payload) = u32::try_from(payload) { + if (libm::copysignf(result, -1.0).to_bits() & payload) == 0 { + set_endptr(endptr, nptr); + return f32::from_bits(result.to_bits() | payload); + } + } + } else { + set_endptr(endptr, nptr); + return result; + } + } + } + + set_endptr(endptr, orig); + 0.0 +} From b9116493ca52643a998dc1e2a4a48158c411f094 Mon Sep 17 00:00:00 2001 From: Myung Gyungmin Date: Wed, 17 Jan 2024 16:08:38 +0900 Subject: [PATCH 02/20] fix feature dependency --- Cargo.toml | 31 +++-- src/abs.rs | 15 ++- src/atof.rs | 131 --------------------- src/atoi.rs | 93 --------------- src/ctype.rs | 67 +++++++++++ src/errno.rs | 1 + src/isdigit.rs | 31 ----- src/isspace.rs | 68 ----------- src/lib.rs | 170 +++------------------------ src/malloc.rs | 0 src/memchr.rs | 44 ------- src/qsort.rs | 93 --------------- src/qsort_r.rs | 88 -------------- src/snprintf.rs | 1 + src/strchr.rs | 15 ++- src/strcmp.rs | 35 ++++++ src/strcpy.rs | 10 +- src/strlen.rs | 14 ++- src/strncmp.rs | 11 +- src/strncpy.rs | 13 ++- src/strstr.rs | 17 ++- src/strtod.rs | 243 --------------------------------------- src/strtof.rs | 55 --------- src/strtol.rs | 297 +++++++++++++++++++++++++++++++++++++++++------- src/strtoul.rs | 191 ------------------------------- 25 files changed, 457 insertions(+), 1277 deletions(-) delete mode 100644 src/atof.rs delete mode 100644 src/atoi.rs create mode 100644 src/ctype.rs delete mode 100644 src/isdigit.rs delete mode 100644 src/isspace.rs delete mode 100644 src/malloc.rs delete mode 100644 src/memchr.rs delete mode 100644 src/qsort.rs delete mode 100644 src/qsort_r.rs delete mode 100644 src/strtod.rs delete mode 100644 src/strtof.rs delete mode 100644 src/strtoul.rs diff --git a/Cargo.toml b/Cargo.toml index 6b74f57..a6f85f0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,8 +9,6 @@ readme = "README.md" repository = "https://github.com/thejpster/tinyrlibc" [dependencies] -libm = "0.2" -critical-section = "1.1.*" [build-dependencies] cc = "1.0" @@ -26,22 +24,21 @@ all = [ "strlen", "strtol", "strtoul", + "strtoll", + "strtoull", + "strtoimax", + "strtoumax", + "strtoul", "strstr", "strchr", "atoi", "itoa", "snprintf", - "qsort", - "qsort_r", - "atof", - "strtod", "errno", - "atol", - "atoll", "isspace", "isdigit", - "strtof", - "memchr", + "isalpha", + "isupper", ] abs = [] strcmp = [] @@ -51,19 +48,17 @@ strncpy = [] strlen = [] strtol = [] strtoul = [] +strtoll = [] +strtoull = [] +strtoimax = [] +strtoumax = [] strstr = [] strchr = [] atoi = [] itoa = [] snprintf = [] -qsort = [] -qsort_r = [] -atof = [] -strtod = [] errno = [] -atol = [] -atoll = [] isspace = [] isdigit = [] -strtof = [] -memchr = [] +isalpha = [] +isupper = [] diff --git a/src/abs.rs b/src/abs.rs index c6d257b..e278067 100644 --- a/src/abs.rs +++ b/src/abs.rs @@ -10,27 +10,36 @@ use crate::CInt; /// use tinyrlibc::abs; /// assert_eq!(abs(-2), 2); /// ``` +#[cfg(feature = "abs")] #[no_mangle] pub extern "C" fn abs(i: CInt) -> CInt { i.abs() } +pub(crate) fn r_abs(i: CInt) -> CInt { + if i < 0 { + -i + } else { + i + } +} + #[cfg(test)] mod test { use super::*; #[test] fn neg() { - assert_eq!(abs(-2), 2); + assert_eq!(r_abs(-2), 2); } #[test] fn pos() { - assert_eq!(abs(3), 3); + assert_eq!(r_abs(3), 3); } #[test] fn zero() { - assert_eq!(abs(0), 0); + assert_eq!(r_abs(0), 0); } } diff --git a/src/atof.rs b/src/atof.rs deleted file mode 100644 index 97205ae..0000000 --- a/src/atof.rs +++ /dev/null @@ -1,131 +0,0 @@ -//! Rust implementation of C library function `atof` -//! -//! Original code from the `c-ward` project. -//! Licensed under the MIT license. - -use crate::{CChar, CDouble, strtod}; - -#[no_mangle] -pub unsafe extern "C" fn atof(nptr: *const CChar) -> CDouble { - strtod(nptr, core::ptr::null_mut()) -} - -#[cfg(test)] -mod test { - #[test] - fn positive() { - let result = unsafe { super::atof(b"123.456\0".as_ptr()) }; - assert_eq!(result, 123.456); - } - - #[test] - fn negative() { - let result = unsafe { super::atof(b"-123.456\0".as_ptr()) }; - assert_eq!(result, -123.456); - } - - #[test] - fn zero() { - let result = unsafe { super::atof(b"0\0".as_ptr()) }; - assert_eq!(result, 0.0); - } - - #[test] - fn nan() { - let result = unsafe { super::atof(b"NaN\0".as_ptr()) }; - assert!(result.is_nan()); - } - - #[test] - fn inf() { - let result = unsafe { super::atof(b"Inf\0".as_ptr()) }; - assert!(result.is_infinite()); - } - - #[test] - fn neg_inf() { - let result = unsafe { super::atof(b"-Inf\0".as_ptr()) }; - assert!(result.is_infinite()); - assert!(result.is_sign_negative()); - } - - #[test] - fn empty() { - let result = unsafe { super::atof(b"\0".as_ptr()) }; - assert!(result == 0.0 ); - } - - #[test] - fn positive_scientific() { - let result = unsafe { super::atof(b"1.23456e2\0".as_ptr()) }; - assert_eq!(result, 123.456); - } - - #[test] - fn negative_scientific() { - let result = unsafe { super::atof(b"1.23456e-2\0".as_ptr()) }; - assert_eq!(result, 0.0123456); - } - - #[test] - fn positive_overflow() { - let result = unsafe { super::atof(b"1e10000\0".as_ptr()) }; - assert!(result.is_infinite()); - } - - #[test] - fn negative_overflow() { - let result = unsafe { super::atof(b"-1e10000\0".as_ptr()) }; - assert!(result.is_infinite()); - assert!(result.is_sign_negative()); - } - - #[test] - fn leading_whitespace() { - let result = unsafe { super::atof(b" \t\n\r123.456\0".as_ptr()) }; - assert_eq!(result, 123.456); - } - - #[test] - fn trailing_whitespace() { - let result = unsafe { super::atof(b"123.456 \t\n\r\0".as_ptr()) }; - assert_eq!(result, 123.456); - } - - #[test] - fn leading_plus() { - let result = unsafe { super::atof(b"+123.456\0".as_ptr()) }; - assert_eq!(result, 123.456); - } - - #[test] - fn leading_minus() { - let result = unsafe { super::atof(b"-123.456\0".as_ptr()) }; - assert_eq!(result, -123.456); - } - - #[test] - fn leading_plus_nan() { - let result = unsafe { super::atof(b"+NaN\0".as_ptr()) }; - assert!(result.is_nan()); - } - - #[test] - fn leading_minus_nan() { - let result = unsafe { super::atof(b"-NaN\0".as_ptr()) }; - assert!(result.is_nan()); - } - - #[test] - fn leading_plus_inf() { - let result = unsafe { super::atof(b"+Inf\0".as_ptr()) }; - assert!(result.is_infinite()); - } - - #[test] - fn leading_minus_inf() { - let result = unsafe { super::atof(b"-Inf\0".as_ptr()) }; - assert!(result.is_infinite()); - assert!(result.is_sign_negative()); - } -} diff --git a/src/atoi.rs b/src/atoi.rs deleted file mode 100644 index d8c2e1e..0000000 --- a/src/atoi.rs +++ /dev/null @@ -1,93 +0,0 @@ -//! Rust implementation of C library function `atoi` -//! -//! Copyright (c) Jonathan 'theJPster' Pallant 2019 -//! Licensed under the Blue Oak Model Licence 1.0.0 - -use crate::{CChar, CInt, CLong, CLongLong, isspace, isdigit}; - -use core::ops::{MulAssign, Neg, SubAssign}; - -#[no_mangle] -pub unsafe extern "C" fn atoi(s: *const CChar) -> CInt { - _atoi(s) -} - -#[no_mangle] -pub unsafe extern "C" fn atol(s: *const CChar) -> CLong { - _atoi(s) -} - -#[no_mangle] -pub unsafe extern "C" fn atoll(s: *const CChar) -> CLongLong { - _atoi(s) -} - -unsafe fn _atoi + From + Default>( - mut s: *const CChar, -) -> T { - let mut negate = false; - let mut n = T::default(); - - // Skip leading whitespace. - while isspace(*s as CInt) != 0 { - s = s.add(1); - } - - // Handle a sign. - match *s as u8 { - b'-' => { - negate = true; - s = s.add(1); - } - b'+' => { - s = s.add(1); - } - _ => {} - } - - // Handle digits. - while isdigit(*s as CInt) != 0 { - n *= T::from(10u8); - n -= (*s as u8 - b'0').into(); - s = s.add(1); - } - - if !negate { - n = -n; - } - - n -} - -#[cfg(test)] -mod test { - #[test] - fn positive() { - let result = unsafe { super::atoi(b"123\0".as_ptr()) }; - assert_eq!(result, 123); - } - - #[test] - fn negative() { - let result = unsafe { super::atoi(b"-123\0".as_ptr()) }; - assert_eq!(result, -123); - } - - #[test] - fn zero() { - let result = unsafe { super::atoi(b"0\0".as_ptr()) }; - assert_eq!(result, 0); - } - - #[test] - fn leading_whitespace() { - let result = unsafe { super::atoi(b" \t\n\r123\0".as_ptr()) }; - assert_eq!(result, 123); - } - - #[test] - fn trailing_whitespace() { - let result = unsafe { super::atoi(b"123 \t\n\r\0".as_ptr()) }; - assert_eq!(result, 123); - } -} diff --git a/src/ctype.rs b/src/ctype.rs new file mode 100644 index 0000000..de79efa --- /dev/null +++ b/src/ctype.rs @@ -0,0 +1,67 @@ +//! A tiny C library, written in Rust. +//! +//! See README.md for more details. +//! +//! This file is Copyright (c) Jonathan 'theJPster' Pallant 2019 +//! Licensed under the Blue Oak Model Licence 1.0.0 +//! +//! See each module for its respective licence. + +/// `void` +pub type CVoid = ::core::ffi::c_void; + +/// `long long int` +pub type CLongLong = ::core::ffi::c_longlong; + +/// `unsigned long long int` +pub type CULongLong = ::core::ffi::c_ulonglong; + +/// `intmax_t` +pub type CUIntMax = CULongLong; + +/// `uintmax_t` +pub type CIntMax = CLongLong; + +/// `long int` +pub type CLong = ::core::ffi::c_long; + +/// `unsigned long int` +pub type CULong = ::core::ffi::c_ulong; + +/// `int` +pub type CInt = ::core::ffi::c_int; + +/// `unsigned int` +pub type CUInt = ::core::ffi::c_uint; + +/// Represents an 8-bit `char`. Rust does not (and will never) support +/// platforms where `char` is not 8-bits long. +pub type CChar = u8; + +/// This allows you to iterate a null-terminated string in a relatively simple +/// way. +pub struct CStringIter { + ptr: *const CChar, + idx: isize, +} + +impl CStringIter { + /// Create a new iterator from a pointer to a null-terminated string. The + /// behaviour is undefined if the string is not null-terminated. + pub fn new(s: *const CChar) -> CStringIter { + CStringIter { ptr: s, idx: 0 } + } +} + +impl core::iter::Iterator for CStringIter { + type Item = CChar; + fn next(&mut self) -> Option { + let c = unsafe { *self.ptr.offset(self.idx) }; + if c == 0 { + None + } else { + self.idx += 1; + Some(c) + } + } +} diff --git a/src/errno.rs b/src/errno.rs index f272f1e..8d8452e 100644 --- a/src/errno.rs +++ b/src/errno.rs @@ -17,6 +17,7 @@ pub fn errno(errno: CInt) -> Errno{ Errno { errno } } +// TODO: implement errno pub fn set_errno(errno: Errno) {} pub const EPERM: CInt = 1; diff --git a/src/isdigit.rs b/src/isdigit.rs deleted file mode 100644 index f42b73d..0000000 --- a/src/isdigit.rs +++ /dev/null @@ -1,31 +0,0 @@ -//! Rust implementation of C library function `isspace` -//! -//! Author: Gyungmin Myung -//! Licensed under the MIT license. - -use crate::CInt; - -#[no_mangle] -pub extern "C" fn isdigit(c: CInt) -> CInt { - CInt::from(c >= CInt::from(b'0') && c <= CInt::from(b'9')) -} - -#[cfg(test)] -mod test { - use crate::CInt; - #[test] - fn all_digits() { - for i in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'] { - let result = super::isdigit(i as CInt); - assert_eq!(result, 1); - } - } - - #[test] - fn non_digits() { - for i in ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'] { - let result = super::isdigit(i as CInt); - assert_eq!(result, 0); - } - } -} diff --git a/src/isspace.rs b/src/isspace.rs deleted file mode 100644 index 947a319..0000000 --- a/src/isspace.rs +++ /dev/null @@ -1,68 +0,0 @@ -//! Rust implementation of C library function `isspace` -//! -//! Author: Gyungmin Myung -//! Licensed under the MIT license. - -use crate::{CInt, CChar}; -#[no_mangle] -pub extern "C" fn isspace(c: CInt) -> CInt { - CInt::from( - c == CInt::from(b' ') - || c == CInt::from(b'\t') - || c == CInt::from(b'\n') - || c == CInt::from(b'\r') - || c == 0x0b - || c == 0x0c, - ) -} - -#[cfg(test)] -mod test { - #[test] - fn space() { - let result = super::isspace(' ' as i32); - assert_eq!(result, 1); - } - - #[test] - fn tab() { - let result = super::isspace('\t' as i32); - assert_eq!(result, 1); - } - - #[test] - fn newline() { - let result = super::isspace('\n' as i32); - assert_eq!(result, 1); - } - - #[test] - fn carriage_return() { - let result = super::isspace('\r' as i32); - assert_eq!(result, 1); - } - - #[test] - fn form_feed() { - let result = super::isspace('\x0c' as i32); - assert_eq!(result, 1); - } - - #[test] - fn vertical_tab() { - let result = super::isspace('\x0b' as i32); - assert_eq!(result, 1); - } - - #[test] - fn zero() { - let result = super::isspace('0' as i32); - assert_eq!(result, 0); - } - - #[test] - fn a() { - let result = super::isspace('a' as i32); - assert_eq!(result, 0); - } -} diff --git a/src/lib.rs b/src/lib.rs index a21d14d..acc5181 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,176 +13,40 @@ #[allow(unused_imports)] use std as core; -#[cfg(feature = "abs")] +mod errno; +pub use self::errno::*; + +mod itoa; +pub use self::itoa::*; + mod abs; -#[cfg(feature = "abs")] -pub use self::abs::abs; +pub use self::abs::*; -#[cfg(feature = "strcmp")] mod strcmp; -#[cfg(feature = "strcmp")] -pub use self::strcmp::strcmp; +pub use self::strcmp::*; -#[cfg(feature = "strncmp")] mod strncmp; -#[cfg(feature = "strncmp")] -pub use self::strncmp::strncmp; +pub use self::strncmp::*; -#[cfg(feature = "strcpy")] mod strcpy; -#[cfg(feature = "strcpy")] -pub use self::strcpy::strcpy; +pub use self::strcpy::*; -#[cfg(feature = "strncpy")] mod strncpy; -#[cfg(feature = "strncpy")] -pub use self::strncpy::strncpy; +pub use self::strncpy::*; -#[cfg(feature = "strlen")] mod strlen; -#[cfg(feature = "strlen")] -pub use self::strlen::strlen; +pub use self::strlen::*; -#[cfg(feature = "strtol")] mod strtol; -#[cfg(feature = "strtol")] -pub use self::strtol::strtol; +pub use self::strtol::*; -#[cfg(feature = "strtoul")] -mod strtoul; -#[cfg(feature = "strtoul")] -pub use self::strtoul::strtoul; - -#[cfg(feature = "strstr")] mod strstr; -#[cfg(feature = "strstr")] -pub use self::strstr::strstr; +pub use self::strstr::*; -#[cfg(feature = "strchr")] mod strchr; -#[cfg(feature = "strchr")] -pub use self::strchr::strchr; - -#[cfg(any(feature = "atoi", feature = "atol", feature = "atoll"))] -mod atoi; -#[cfg(feature = "atoi")] -pub use self::atoi::atoi; -#[cfg(feature = "atol")] -pub use self::atoi::atol; -#[cfg(feature = "atoll")] -pub use self::atoi::atoll; - -#[cfg(feature = "itoa")] -mod itoa; -#[cfg(feature = "itoa")] -pub use self::itoa::itoa; +pub use self::strchr::*; -#[cfg(feature = "snprintf")] mod snprintf; -#[cfg(feature = "qsort")] -mod qsort; -#[cfg(feature = "qsort")] -pub use self::qsort::qsort; - -#[cfg(feature = "qsort_r")] -mod qsort_r; -#[cfg(feature = "qsort_r")] -pub use self::qsort_r::qsort_r; - -#[cfg(feature = "atof")] -mod atof; -#[cfg(feature = "atof")] -pub use self::atof::atof; - -#[cfg(feature = "strtod")] -mod strtod; -#[cfg(feature = "strtod")] -pub use self::strtod::strtod; - -#[cfg(feature = "strtof")] -mod strtof; -#[cfg(feature = "strtof")] -pub use self::strtof::strtof; - -#[cfg(feature = "isspace")] -mod isspace; -#[cfg(feature = "isspace")] -pub use self::isspace::isspace; - -#[cfg(feature = "isdigit")] -mod isdigit; -#[cfg(feature = "isdigit")] -pub use self::isdigit::isdigit; - -#[cfg(feature = "errno")] -mod errno; -#[cfg(feature = "errno")] -pub use self::errno::{errno, set_errno}; - -#[cfg(feature = "memchr")] -mod memchr; -#[cfg(feature = "memchr")] -pub use self::memchr::memchr; - -/// `void` -pub type CVoid = ::core::ffi::c_void; - -/// `size_t` -pub type CSizeT = usize; - -/// `double` -pub type CDouble = core::ffi::c_double; - -/// `float` -pub type CFloat = core::ffi::c_float; - -/// `long long int` -pub type CLongLong = ::core::ffi::c_longlong; - -/// `unsigned long long int` -pub type CULongLong = ::core::ffi::c_ulonglong; - -/// `long int` -pub type CLong = ::core::ffi::c_long; - -/// `unsigned long int` -pub type CULong = ::core::ffi::c_ulong; - -/// `int` -pub type CInt = ::core::ffi::c_int; - -/// `unsigned int` -pub type CUInt = ::core::ffi::c_uint; - -/// Represents an 8-bit `char`. Rust does not (and will never) support -/// platforms where `char` is not 8-bits long. -pub type CChar = u8; - -/// This allows you to iterate a null-terminated string in a relatively simple -/// way. -pub struct CStringIter { - ptr: *const CChar, - idx: isize, -} - -impl CStringIter { - /// Create a new iterator from a pointer to a null-terminated string. The - /// behaviour is undefined if the string is not null-terminated. - pub fn new(s: *const CChar) -> CStringIter { - CStringIter { ptr: s, idx: 0 } - } -} - -impl core::iter::Iterator for CStringIter { - type Item = CChar; - fn next(&mut self) -> Option { - let c = unsafe { *self.ptr.offset(self.idx) }; - if c == 0 { - None - } else { - self.idx += 1; - Some(c) - } - } -} +mod ctype; +pub use self::ctype::*; diff --git a/src/malloc.rs b/src/malloc.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/memchr.rs b/src/memchr.rs deleted file mode 100644 index e4d3272..0000000 --- a/src/memchr.rs +++ /dev/null @@ -1,44 +0,0 @@ -//! Rust implementation of C library function `memchr` -//! -//! Author: Gyungmin Myung -//! Licensed under the MIT license. - -use crate::{CInt, CVoid, CSizeT}; - -#[no_mangle] -pub unsafe extern "C" fn memchr( - s: *const CVoid, - c: CInt, - n: CSizeT, -) -> *mut CVoid { - core::slice::from_raw_parts(s.cast::(), n as usize) - .iter() - .position(|&x| x == c as u8) - .map_or(core::ptr::null_mut(), |x| s.add(x) as _) -} - -#[cfg(test)] -mod test { - use crate::{CInt, CSizeT}; - - #[test] - fn find_char() { - let s = b"hello world\0"; - let result = unsafe { super::memchr(s.as_ptr().cast(), 'w' as CInt, 12 as CSizeT) }; - assert_eq!(result as *const _, s.as_ptr().wrapping_add(6)); - } - - #[test] - fn find_null() { - let s = b"hello world\0"; - let result = unsafe { super::memchr(s.as_ptr().cast(), 0, 12 as CSizeT) }; - assert_eq!(result as *const _, s.as_ptr().wrapping_add(11)); - } - - #[test] - fn find_nothing() { - let s = b"hello world\0"; - let result = unsafe { super::memchr(s.as_ptr().cast(), 'z' as CInt, 12 as CSizeT) }; - assert_eq!(result, core::ptr::null_mut()); - } -} diff --git a/src/qsort.rs b/src/qsort.rs deleted file mode 100644 index 0c199b4..0000000 --- a/src/qsort.rs +++ /dev/null @@ -1,93 +0,0 @@ -//! Rust implementation of C library function `qsort` -//! -//! Original code from the `c-ward` project. -//! Licensed under the MIT license. - -use crate::{CVoid, CInt, CSizeT}; - -#[no_mangle] -pub unsafe extern "C" fn qsort( - base: *mut CVoid, - nmemb: CSizeT, - width: CSizeT, - compar: Option CInt>, -) { - - let compar = compar.unwrap(); - - if nmemb <= 1 { - return; - } - - let base = base.cast::(); - let mut gap = nmemb; - - loop { - gap = next_gap(gap); - - let mut any_swapped = false; - let mut a = base; - let mut b = base.add(gap * width); - for _ in 0..nmemb - gap { - if compar(a.cast(), b.cast()) > 0 { - swap(a, b, width); - any_swapped = true; - } - a = a.add(width); - b = b.add(width); - } - - if gap <= 1 && !any_swapped { - break; - } - } -} - -fn next_gap(gap: CSizeT) -> CSizeT { - let gap = (gap * 10) / 13; - - if gap == 9 || gap == 10 { - 11 // apply the "rule of 11" - } else if gap <= 1 { - 1 - } else { - gap - } -} - -unsafe fn swap(a: *mut u8, b: *mut u8, width: CSizeT) { - for i in 0..width { - core::ptr::swap(a.add(i), b.add(i)); - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn test_qsort() { - let mut arr = [5, 4, 3, 2, 1]; - unsafe { - qsort( - arr.as_mut_ptr().cast(), - arr.len(), - core::mem::size_of::(), - Some(cmp), - ); - } - assert_eq!(arr, [1, 2, 3, 4, 5]); - } - - unsafe extern "C" fn cmp(a: *const CVoid, b: *const CVoid) -> CInt { - let a = a.cast::(); - let b = b.cast::(); - if *a < *b { - -1 - } else if *a > *b { - 1 - } else { - 0 - } - } -} diff --git a/src/qsort_r.rs b/src/qsort_r.rs deleted file mode 100644 index 7096d4b..0000000 --- a/src/qsort_r.rs +++ /dev/null @@ -1,88 +0,0 @@ -//! Rust implementation of C library function `qsort_r` -//! -//! Original code from the `c-ward` project. -//! Licensed under the MIT license. - -use crate::{CVoid, CInt, CSizeT}; - -#[no_mangle] -pub unsafe extern "C" fn qsort_r( - base: *mut CVoid, - nmemb: CSizeT, - width: CSizeT, - compar: Option CInt>, - arg: *mut CVoid, -) { - let compar = compar.unwrap(); - - if nmemb <= 1 { - return; - } - - let base = base.cast::(); - let mut gap = nmemb; - - loop { - gap = next_gap(gap); - - let mut any_swapped = false; - let mut a = base; - let mut b = base.add(gap * width); - for _ in 0..nmemb - gap { - if compar(a.cast(), b.cast(), arg) > 0 { - swap(a, b, width); - any_swapped = true; - } - a = a.add(width); - b = b.add(width); - } - - if gap <= 1 && !any_swapped { - break; - } - } -} - -fn next_gap(gap: CSizeT) -> CSizeT { - let gap = (gap * 10) / 13; - - if gap == 9 || gap == 10 { - 11 // apply the "rule of 11" - } else if gap <= 1 { - 1 - } else { - gap - } -} - -unsafe fn swap(a: *mut u8, b: *mut u8, width: CSizeT) { - for i in 0..width { - core::ptr::swap(a.add(i), b.add(i)); - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn test_qsort() { - let mut data = [5, 4, 3, 2, 1]; - unsafe { - qsort_r( - data.as_mut_ptr().cast(), - data.len(), - core::mem::size_of::(), - Some(cmp_r), - core::ptr::null_mut(), - ); - } - assert_eq!(data, [1, 2, 3, 4, 5]); - } - - unsafe extern "C" fn cmp_r(a: *const CVoid, b: *const CVoid, _: *mut CVoid) -> CInt { - let a = a.cast::(); - let b = b.cast::(); - (*a).cmp(&*b) as CInt - } -} diff --git a/src/snprintf.rs b/src/snprintf.rs index ac4be00..354b4a2 100644 --- a/src/snprintf.rs +++ b/src/snprintf.rs @@ -3,6 +3,7 @@ //! Copyright (c) Jonathan 'theJPster' Pallant 2019 //! Licensed under the Blue Oak Model Licence 1.0.0 +#[cfg(feature = "snprintf")] #[cfg(test)] mod test { extern "C" { diff --git a/src/strchr.rs b/src/strchr.rs index 5b77ed6..0b140c8 100644 --- a/src/strchr.rs +++ b/src/strchr.rs @@ -6,8 +6,13 @@ use crate::{CChar, CInt}; /// Rust implementation of C library function `strchr` +#[cfg(feature = "strchr")] #[no_mangle] pub unsafe extern "C" fn strchr(haystack: *const CChar, needle: CInt) -> *const CChar { + r_strchr(haystack, needle) +} + +pub unsafe fn r_strchr(haystack: *const CChar, needle: CInt) -> *const CChar { for idx in 0.. { let ptr = haystack.offset(idx); if needle == (*ptr) as CInt { @@ -27,35 +32,35 @@ mod test { #[test] fn no_match() { let haystack = b"haystack\0".as_ptr(); - let result = unsafe { strchr(haystack, b'X' as CInt) }; + let result = unsafe { r_strchr(haystack, b'X' as CInt) }; assert_eq!(result, core::ptr::null()); } #[test] fn null() { let haystack = b"haystack\0".as_ptr(); - let result = unsafe { strchr(haystack, 0) }; + let result = unsafe { r_strchr(haystack, 0) }; assert_eq!(result, unsafe { haystack.offset(8) }); } #[test] fn start() { let haystack = b"haystack\0".as_ptr(); - let result = unsafe { strchr(haystack, b'h' as CInt) }; + let result = unsafe { r_strchr(haystack, b'h' as CInt) }; assert_eq!(result, haystack); } #[test] fn middle() { let haystack = b"haystack\0".as_ptr(); - let result = unsafe { strchr(haystack, b'y' as CInt) }; + let result = unsafe { r_strchr(haystack, b'y' as CInt) }; assert_eq!(result, unsafe { haystack.offset(2) }); } #[test] fn end() { let haystack = b"haystack\0".as_ptr(); - let result = unsafe { strchr(haystack, b'k' as CInt) }; + let result = unsafe { r_strchr(haystack, b'k' as CInt) }; assert_eq!(result, unsafe { haystack.offset(7) }); } } diff --git a/src/strcmp.rs b/src/strcmp.rs index bd5c50f..3dec3e7 100644 --- a/src/strcmp.rs +++ b/src/strcmp.rs @@ -7,7 +7,12 @@ use crate::{CChar, CInt}; /// Rust implementation of C library function `strcmp` #[no_mangle] +#[cfg(feature = "strcmp")] pub unsafe extern "C" fn strcmp(s1: *const CChar, s2: *const CChar) -> CInt { + r_strcmp(s1, s2) +} + +pub unsafe fn r_strcmp(s1: *const CChar, s2: *const CChar) -> CInt { for i in 0.. { let s1_i = s1.offset(i); let s2_i = s2.offset(i); @@ -19,3 +24,33 @@ pub unsafe extern "C" fn strcmp(s1: *const CChar, s2: *const CChar) -> CInt { } 0 } + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test1() { + assert!(unsafe { r_strcmp(b"Hello\0" as *const CChar, b"Hello\0" as *const CChar) } == 0); + } + + #[test] + fn test2() { + assert!(unsafe { r_strcmp(b"Hello\0" as *const CChar, b"Hello1\0" as *const CChar) } < 0); + } + + #[test] + fn test3() { + assert!(unsafe { r_strcmp(b"Hello1\0" as *const CChar, b"Hello\0" as *const CChar) } > 0); + } + + #[test] + fn test4() { + assert!(unsafe { r_strcmp(b"\0" as *const CChar, b"Hello\0" as *const CChar) } < 0); + } + + #[test] + fn test5() { + assert!(unsafe { r_strcmp(b"Hello\0" as *const CChar, b"\0" as *const CChar) } > 0); + } +} diff --git a/src/strcpy.rs b/src/strcpy.rs index a902ca6..6cc8e09 100644 --- a/src/strcpy.rs +++ b/src/strcpy.rs @@ -6,8 +6,14 @@ use crate::CChar; /// Rust implementation of C library function `strcpy`. Passing NULL /// (core::ptr::null()) gives undefined behaviour. +#[cfg(feature = "strcpy")] #[no_mangle] pub unsafe extern "C" fn strcpy(dest: *mut CChar, src: *const CChar) -> *const CChar { + r_strcpy(dest, src) +} + + +pub unsafe fn r_strcpy(dest: *mut CChar, src: *const CChar) -> *const CChar { let mut i = 0; loop { *dest.offset(i) = *src.offset(i); @@ -28,7 +34,7 @@ mod test { fn short() { let src = b"hi\0"; let mut dest = *b"abcdef"; // no null terminator - let result = unsafe { strcpy(dest.as_mut_ptr(), src.as_ptr()) }; + let result = unsafe { r_strcpy(dest.as_mut_ptr(), src.as_ptr()) }; assert_eq!( unsafe { core::slice::from_raw_parts(result, 5) }, *b"hi\0de" @@ -39,7 +45,7 @@ mod test { fn two() { let src = b"hi\0"; let mut dest = [0u8; 2]; // no space for null terminator - let result = unsafe { strcpy(dest.as_mut_ptr(), src.as_ptr()) }; + let result = unsafe { r_strcpy(dest.as_mut_ptr(), src.as_ptr()) }; assert_eq!(unsafe { core::slice::from_raw_parts(result, 2) }, b"hi"); } } diff --git a/src/strlen.rs b/src/strlen.rs index fc2683d..335f4f4 100644 --- a/src/strlen.rs +++ b/src/strlen.rs @@ -6,8 +6,14 @@ use crate::CChar; /// Rust implementation of C library function `strlen` +#[cfg(feature = "strlen")] #[no_mangle] -pub unsafe extern "C" fn strlen(mut s: *const CChar) -> usize { +pub unsafe extern "C" fn strlen(s: *const CChar) -> usize { + r_strlen(s) +} + + +pub unsafe fn r_strlen(mut s: *const CChar) -> usize { let mut result = 0; while *s != 0 { s = s.offset(1); @@ -22,16 +28,16 @@ mod test { #[test] fn test1() { - assert_eq!(unsafe { strlen(b"Hello\0" as *const CChar) }, 5); + assert_eq!(unsafe { r_strlen(b"Hello\0" as *const CChar) }, 5); } #[test] fn test2() { - assert_eq!(unsafe { strlen(b"\0" as *const CChar) }, 0); + assert_eq!(unsafe { r_strlen(b"\0" as *const CChar) }, 0); } #[test] fn test3() { - assert_eq!(unsafe { strlen(b"X\0" as *const CChar) }, 1); + assert_eq!(unsafe { r_strlen(b"X\0" as *const CChar) }, 1); } } diff --git a/src/strncmp.rs b/src/strncmp.rs index 498ab03..c7439df 100644 --- a/src/strncmp.rs +++ b/src/strncmp.rs @@ -7,8 +7,13 @@ use crate::{CChar, CInt}; /// Rust implementation of C library function `strncmp`. Passing NULL /// (core::ptr::null()) gives undefined behaviour. +#[cfg(feature = "strncmp")] #[no_mangle] pub unsafe extern "C" fn strncmp(s1: *const CChar, s2: *const CChar, n: usize) -> crate::CInt { + r_strncmp(s1, s2, n) +} + +pub(crate) unsafe fn r_strncmp(s1: *const CChar, s2: *const CChar, n: usize) -> crate::CInt { for i in 0..n as isize { let s1_i = s1.offset(i); let s2_i = s2.offset(i); @@ -29,7 +34,7 @@ mod test { fn matches() { let a = b"123\0"; let b = b"1234\0"; - let result = unsafe { strncmp(a.as_ptr(), b.as_ptr(), 3) }; + let result = unsafe { r_strncmp(a.as_ptr(), b.as_ptr(), 3) }; // Match! assert_eq!(result, 0); } @@ -38,7 +43,7 @@ mod test { fn no_match() { let a = b"123\0"; let b = b"x1234\0"; - let result = unsafe { strncmp(a.as_ptr(), b.as_ptr(), 3) }; + let result = unsafe { r_strncmp(a.as_ptr(), b.as_ptr(), 3) }; // No match, first string first assert!(result < 0); } @@ -47,7 +52,7 @@ mod test { fn no_match2() { let a = b"bbbbb\0"; let b = b"aaaaa\0"; - let result = unsafe { strncmp(a.as_ptr(), b.as_ptr(), 3) }; + let result = unsafe { r_strncmp(a.as_ptr(), b.as_ptr(), 3) }; // No match, second string first assert!(result > 0); } diff --git a/src/strncpy.rs b/src/strncpy.rs index 4b325fc..0ab980a 100644 --- a/src/strncpy.rs +++ b/src/strncpy.rs @@ -7,8 +7,17 @@ use crate::CChar; /// Rust implementation of C library function `strncmp`. Passing NULL /// (core::ptr::null()) gives undefined behaviour. +#[cfg(feature = "strncpy")] #[no_mangle] pub unsafe extern "C" fn strncpy( + dest: *mut CChar, + src: *const CChar, + count: usize, +) -> *const CChar { + r_strncpy(dest, src, count) +} + +pub(crate) unsafe fn r_strncpy( dest: *mut CChar, src: *const CChar, count: usize, @@ -36,7 +45,7 @@ mod test { fn short() { let src = b"hi\0"; let mut dest = *b"abcdef"; // no null terminator - let result = unsafe { strncpy(dest.as_mut_ptr(), src.as_ptr(), 5) }; + let result = unsafe { r_strncpy(dest.as_mut_ptr(), src.as_ptr(), 5) }; assert_eq!( unsafe { core::slice::from_raw_parts(result, 5) }, *b"hi\0\0\0" @@ -47,7 +56,7 @@ mod test { fn two() { let src = b"hi\0"; let mut dest = [0u8; 2]; // no space for null terminator - let result = unsafe { strncpy(dest.as_mut_ptr(), src.as_ptr(), 2) }; + let result = unsafe { r_strncpy(dest.as_mut_ptr(), src.as_ptr(), 2) }; assert_eq!(unsafe { core::slice::from_raw_parts(result, 2) }, b"hi"); } } diff --git a/src/strstr.rs b/src/strstr.rs index cb4ac9e..29d7243 100644 --- a/src/strstr.rs +++ b/src/strstr.rs @@ -5,9 +5,14 @@ use crate::{CChar, CStringIter}; -/// Rust implementation of C library function `strstr` +#[cfg(feature = "strstr")] #[no_mangle] pub unsafe extern "C" fn strstr(haystack: *const CChar, needle: *const CChar) -> *const CChar { + r_strstr(haystack, needle) +} + +/// Rust implementation of C library function `strstr` +pub(crate) unsafe extern "C" fn r_strstr(haystack: *const CChar, needle: *const CChar) -> *const CChar { if *needle.offset(0) == 0 { return haystack; } @@ -38,7 +43,7 @@ mod test { fn no_match() { let needle = b"needle\0".as_ptr(); let haystack = b"haystack\0".as_ptr(); - let result = unsafe { strstr(haystack, needle) }; + let result = unsafe { r_strstr(haystack, needle) }; assert_eq!(result, core::ptr::null()); } @@ -46,7 +51,7 @@ mod test { fn start() { let needle = b"hay\0".as_ptr(); let haystack = b"haystack\0".as_ptr(); - let result = unsafe { strstr(haystack, needle) }; + let result = unsafe { r_strstr(haystack, needle) }; assert_eq!(result, haystack); } @@ -54,7 +59,7 @@ mod test { fn middle() { let needle = b"yst\0".as_ptr(); let haystack = b"haystack\0".as_ptr(); - let result = unsafe { strstr(haystack, needle) }; + let result = unsafe { r_strstr(haystack, needle) }; assert_eq!(result, unsafe { haystack.offset(2) }); } @@ -62,7 +67,7 @@ mod test { fn end() { let needle = b"stack\0".as_ptr(); let haystack = b"haystack\0".as_ptr(); - let result = unsafe { strstr(haystack, needle) }; + let result = unsafe { r_strstr(haystack, needle) }; assert_eq!(result, unsafe { haystack.offset(3) }); } @@ -70,7 +75,7 @@ mod test { fn partial() { let needle = b"haystacka\0".as_ptr(); let haystack = b"haystack\0".as_ptr(); - let result = unsafe { strstr(haystack, needle) }; + let result = unsafe { r_strstr(haystack, needle) }; assert_eq!(result, core::ptr::null()); } } diff --git a/src/strtod.rs b/src/strtod.rs deleted file mode 100644 index 6621d64..0000000 --- a/src/strtod.rs +++ /dev/null @@ -1,243 +0,0 @@ -//! Rust implementation of C library function `strtod` -//! -//! Original code from the `c-ward` project. -//! Licensed under the MIT license. - -use crate::{CChar, CFloat, CDouble, errno::*}; -use core::str::FromStr; -use core::{slice, str}; - -#[no_mangle] -pub unsafe extern "C" fn strtod(nptr: *const CChar, endptr: *mut *mut CChar) -> CDouble { - let nptr = nptr.cast::(); - let orig = nptr; - let (nptr, format) = scan_float(nptr); - let s = make_str(orig, nptr); - - match format { - Format::Hexadecimal(_any_nonzero) => { - todo!("hexadecimal float parsing") - } - Format::Decimal(any_nonzero) => { - if let Ok(f) = f64::from_str(s) { - set_endptr(endptr, nptr); - set_errno_f64(f, any_nonzero); - return f; - } - } - Format::Infinity => { - set_endptr(endptr, nptr); - return if s.starts_with('-') { - -f64::INFINITY - } else { - f64::INFINITY - }; - } - Format::NaN(payload) => { - let result = if s.starts_with('-') { - libm::copysign(f64::NAN, -1.0) - } else { - libm::copysign(f64::NAN, 1.0) - }; - if let Some(payload) = payload { - if (libm::copysign(result, -1.0).to_bits() & payload) == 0 { - set_endptr(endptr, nptr); - return f64::from_bits(result.to_bits() | payload); - } - } else { - set_endptr(endptr, nptr); - return result; - } - } - } - - set_endptr(endptr, orig); - 0.0 -} - -pub unsafe fn make_str<'a>(start: *const u8, nptr: *const u8) -> &'a str { - str::from_utf8_unchecked(slice::from_raw_parts( - start, - nptr.offset_from(start) as usize, - )) - .trim_start() -} - -pub unsafe fn set_endptr(endptr: *mut *mut CChar, nptr: *const u8) { - if !endptr.is_null() { - *endptr = nptr.cast_mut().cast(); - } -} - -// If we tried to parse a number but got infinity, or if the number was -// subnormal, or we saw non-zero digits but got zero, set errno. -pub fn set_errno_f32(f: f32, any_nonzero: bool) { - if f.is_infinite() || f.is_subnormal() || (f == 0.0 && any_nonzero) { - set_errno(errno(ERANGE)); - } -} - -fn set_errno_f64(f: f64, any_nonzero: bool) { - if f.is_infinite() || f.is_subnormal() || (f == 0.0 && any_nonzero) { - set_errno(errno(ERANGE)); - } -} - -pub unsafe fn scan_float(mut nptr: *const u8) -> (*const u8, Format) { - while (*nptr).is_ascii_whitespace() { - nptr = nptr.add(1); - } - - if *nptr == b'-' || *nptr == b'+' { - nptr = nptr.add(1); - } - - if (*nptr).to_ascii_lowercase() == b'i' - && (*nptr.add(1)).to_ascii_lowercase() == b'n' - && (*nptr.add(2)).to_ascii_lowercase() == b'f' - { - nptr = nptr.add(3); - if (*nptr).to_ascii_lowercase() == b'i' - && (*nptr.add(1)).to_ascii_lowercase() == b'n' - && (*nptr.add(2)).to_ascii_lowercase() == b'i' - && (*nptr.add(3)).to_ascii_lowercase() == b't' - && (*nptr.add(4)).to_ascii_lowercase() == b'y' - { - nptr = nptr.add(5); - } - return (nptr, Format::Infinity); - } - - if (*nptr).to_ascii_lowercase() == b'n' - && (*nptr.add(1)).to_ascii_lowercase() == b'a' - && (*nptr.add(2)).to_ascii_lowercase() == b'n' - { - nptr = nptr.add(3); - if *nptr == b'(' { - let paren = nptr; - nptr = nptr.add(1); - let payload = if *nptr == b'0' && (*nptr.add(1)).to_ascii_lowercase() == b'x' { - nptr = nptr.add(2); - let start = nptr; - while (*nptr).is_ascii_hexdigit() { - nptr = nptr.add(1); - } - let s = make_str(start, nptr); - if s.is_empty() { - 0 - } else { - u64::from_str_radix(s, 16).unwrap() - } - } else { - let start = nptr; - while (*nptr).is_ascii_digit() { - nptr = nptr.add(1); - } - let s = make_str(start, nptr); - if s.is_empty() { - 0 - } else { - s.parse().unwrap() - } - }; - if *nptr == b')' { - nptr = nptr.add(1); - return (nptr, Format::NaN(Some(payload))); - } - nptr = paren; - } - return (nptr, Format::NaN(None)); - } - - let mut hex = false; - if *nptr == b'0' { - nptr = nptr.add(1); - if *nptr == b'x' || *nptr == b'X' { - nptr = nptr.add(1); - hex = true; - } - } - - let mut any_nonzero = false; - if hex { - while (*nptr).is_ascii_hexdigit() { - if *nptr != b'0' { - any_nonzero = true; - } - nptr = nptr.add(1); - } - } else { - while (*nptr).is_ascii_digit() { - if *nptr != b'0' { - any_nonzero = true; - } - nptr = nptr.add(1); - } - } - - if *nptr == b'.' { - nptr = nptr.add(1); - - if hex { - while (*nptr).is_ascii_hexdigit() { - if *nptr != b'0' { - any_nonzero = true; - } - nptr = nptr.add(1); - } - } else { - while (*nptr).is_ascii_digit() { - if *nptr != b'0' { - any_nonzero = true; - } - nptr = nptr.add(1); - } - } - } - - let mut before_exp = None; - if hex { - if *nptr == b'p' || *nptr == b'P' { - before_exp = Some(nptr); - nptr = nptr.add(1); - } - } else { - if *nptr == b'e' || *nptr == b'E' { - before_exp = Some(nptr); - nptr = nptr.add(1); - } - } - - if let Some(before_exp) = before_exp { - if *nptr == b'-' || *nptr == b'+' { - nptr = nptr.add(1); - } - - if (*nptr).is_ascii_digit() { - while (*nptr).is_ascii_digit() { - nptr = nptr.add(1); - } - } else { - nptr = before_exp; - } - } - - ( - nptr, - if hex { - if before_exp.is_none() { - todo!("strtod hexadecimal format with no `p`"); - } - Format::Hexadecimal(any_nonzero) - } else { - Format::Decimal(any_nonzero) - }, - ) -} - -pub enum Format { - Decimal(bool), - Hexadecimal(bool), - Infinity, - NaN(Option), -} diff --git a/src/strtof.rs b/src/strtof.rs deleted file mode 100644 index 1620997..0000000 --- a/src/strtof.rs +++ /dev/null @@ -1,55 +0,0 @@ - -use crate::{CChar, CFloat, CDouble, errno::*, strtod::*}; -use core::str::FromStr; -use core::{slice, str}; - -#[no_mangle] -pub unsafe extern "C" fn strtof(nptr: *const CChar, endptr: *mut *mut CChar) -> CFloat { - - let nptr = nptr.cast::(); - let orig = nptr; - let (nptr, format) = scan_float(nptr); - let s = make_str(orig, nptr); - - match format { - Format::Hexadecimal(_any_nonzero) => { - todo!("hexadecimal float parsing") - } - Format::Decimal(any_nonzero) => { - if let Ok(f) = f32::from_str(s) { - set_endptr(endptr, nptr); - set_errno_f32(f, any_nonzero); - return f; - } - } - Format::Infinity => { - set_endptr(endptr, nptr); - return if s.starts_with('-') { - -f32::INFINITY - } else { - f32::INFINITY - }; - } - Format::NaN(payload) => { - let result = if s.starts_with('-') { - libm::copysignf(f32::NAN, -1.0) - } else { - libm::copysignf(f32::NAN, 1.0) - }; - if let Some(payload) = payload { - if let Ok(payload) = u32::try_from(payload) { - if (libm::copysignf(result, -1.0).to_bits() & payload) == 0 { - set_endptr(endptr, nptr); - return f32::from_bits(result.to_bits() | payload); - } - } - } else { - set_endptr(endptr, nptr); - return result; - } - } - } - - set_endptr(endptr, orig); - 0.0 -} diff --git a/src/strtol.rs b/src/strtol.rs index 602c7e3..52cf37f 100644 --- a/src/strtol.rs +++ b/src/strtol.rs @@ -1,67 +1,276 @@ -//! Rust implementation of C library function `strtol` +//! Rust implementation of C library function `atof` //! -//! Copyright (c) Jonathan 'theJPster' Pallant 2019 -//! Licensed under the Blue Oak Model Licence 1.0.0 +//! Original code from the `c-ward` project. +//! Licensed under the MIT license. -use crate::{CChar, CLong, CStringIter}; +use crate::{errno::*, CChar, CInt, CIntMax, CLong, CLongLong, CUIntMax, CULong, CULongLong}; -/// Rust implementation of C library function `strtol`. -/// -/// Takes a null-terminated string and interprets it as a decimal integer. -/// This integer is returned as a `CLong`. Parsing stops when the first -/// non-digit ASCII byte is seen. If no valid ASCII digit bytes are seen, this -/// function returns zero. +#[cfg(feature = "atoi")] #[no_mangle] -pub unsafe extern "C" fn strtol(s: *const CChar) -> CLong { - let mut result: CLong = 0; - for c in CStringIter::new(s) { - if (b'0'..=b'9').contains(&c) { - result *= 10; - result += (c - b'0') as CLong; +pub unsafe extern "C" fn atoi(s: *const CChar) -> CInt { + r_atoi(s) +} + +#[cfg(feature = "strtol")] +#[no_mangle] +pub unsafe extern "C" fn strtol(s: *const CChar, endptr: *mut *mut CChar, base: CInt) -> CLong { + r_strtol(s, endptr, base) +} + +#[cfg(feature = "strtoul")] +#[no_mangle] +pub unsafe extern "C" fn strtoul(s: *const CChar, endptr: *mut *mut CChar, base: CInt) -> CULong { + r_strtoul(s, endptr, base) +} + +#[cfg(feature = "strtoll")] +#[no_mangle] +pub unsafe extern "C" fn strtoll(s: *const CChar, endptr: *mut *mut CChar, base: CInt) -> CLongLong { + r_strtoll(s, endptr, base) +} + +#[cfg(feature = "strtoull")] +#[no_mangle] +pub unsafe extern "C" fn strtoull(s: *const CChar, endptr: *mut *mut CChar, base: CInt) -> CULongLong { + r_strtoull(s, endptr, base) +} + +#[cfg(feature = "strtoimax")] +#[no_mangle] +pub unsafe extern "C" fn strtoimax(s: *const CChar, endptr: *mut *mut CChar, base: CInt) -> CIntMax { + r_strtoimax(s, endptr, base) +} + +#[cfg(feature = "strtoumax")] +#[no_mangle] +pub unsafe extern "C" fn strtoumax(s: *const CChar, endptr: *mut *mut CChar, base: CInt) -> CUIntMax { + r_strtoumax(s, endptr, base) +} + +pub(crate) unsafe fn r_atoi(s: *const CChar) -> CInt { + r_strtol(s, core::ptr::null_mut(), 10) as CInt +} + +pub(crate) unsafe fn r_strtol(s: *const CChar, endptr: *mut *mut CChar, base: CInt) -> CLong { + r_strtox(s, endptr, base, CLong::MIN, CLong::MAX as _) as CLong +} + +pub(crate) unsafe fn r_strtoul(s: *const CChar, endptr: *mut *mut CChar, base: CInt) -> CULong { + r_strtox(s, endptr, base, 0, CULong::MAX) as CULong +} + +pub(crate) unsafe fn r_strtoll(s: *const CChar, endptr: *mut *mut CChar, base: CInt) -> CLongLong { + r_strtox(s, endptr, base, CLongLong::MIN, CLongLong::MAX as _) as CLongLong +} + +pub(crate) unsafe fn r_strtoull(s: *const CChar, endptr: *mut *mut CChar, base: CInt) -> CULongLong { + r_strtox(s, endptr, base, 0, CULongLong::MAX) as CULongLong +} + +pub(crate) unsafe fn r_strtoimax(s: *const CChar, endptr: *mut *mut CChar, base: CInt) -> CIntMax { + r_strtox(s, endptr, base, CIntMax::MIN, CIntMax::MAX as _) as CIntMax +} + +pub(crate) unsafe fn r_strtoumax(s: *const CChar, endptr: *mut *mut CChar, base: CInt) -> CUIntMax { + r_strtox(s, endptr, base, 0, CUIntMax::MAX) as CUIntMax +} + +pub(crate) unsafe fn r_strtox( + s: *const CChar, + endptr: *mut *mut CChar, + base: CInt, + min: CIntMax, + max: CUIntMax, +) -> CUIntMax { + if base < 0 || base > 36 { + set_errno(errno(EINVAL)); + return 0; + } + + // Skip leading whitespace. + let mut s = s; + while r_isspace(CInt::from(*s)) != 0 { + s = s.add(1); + } + + // Parse an optional +/- sign. + let mut negate = false; + if *s == b'+' as CChar { + s = s.add(1); + } else if *s == b'-' as CChar { + negate = true; + s = s.add(1); + } + + // Parse an optional base prefix. + let mut base: CUIntMax = base as CUIntMax; + if base == 0 { + if *s == b'0' as CChar { + s = s.add(1); + if (*s == b'x' as CChar || *s == b'X' as CChar) && (*s.add(1) as u8).is_ascii_hexdigit() + { + s = s.add(1); + base = 16; + } else { + base = 8; + } } else { - break; + base = 10; } + } else if base == 16 + && *s == b'0' as CChar + && (*s.add(1) == b'x' as CChar || *s.add(1) == b'X' as CChar) + && (*s.add(2) as u8).is_ascii_hexdigit() + { + s = s.add(2); } - result -} -#[cfg(test)] -mod test { - use super::strtol; + // Parse the digits. + let mut overflow = false; + let mut num: CUIntMax = 0; + loop { + let digit: CUIntMax = match *s as u8 { + x @ b'0'..=b'9' => x - b'0', + x @ b'a'..=b'z' => x - b'a' + 10, + x @ b'A'..=b'Z' => x - b'A' + 10, + _ => break, + } + .into(); + if digit >= base { + break; + } - #[test] - fn empty() { - let result = unsafe { strtol(b"\0".as_ptr()) }; - assert_eq!(result, 0); + if negate && min != 0 { + if (num as CIntMax) < min / base as CIntMax { + overflow = true; + } + } else { + if num > max / base { + overflow = true; + } + } + num = num.wrapping_mul(base); + + if negate && min != 0 { + if (num as CIntMax) < min + digit as CIntMax { + overflow = true; + } + num = num.wrapping_sub(digit); + } else { + if num > max - digit { + overflow = true; + } + num = num.wrapping_add(digit); + } + + s = s.add(1); } - #[test] - fn non_digit() { - let result = unsafe { strtol(b"1234x\0".as_ptr()) }; - assert_eq!(result, 1234); + // If requested, report the end position. + if !endptr.is_null() { + *endptr = s.cast_mut(); } - #[test] - fn bad_number() { - let result = unsafe { strtol(b"x\0".as_ptr()) }; - assert_eq!(result, 0); + // Report overflow. + if overflow { + set_errno(errno(ERANGE)); + return if negate && min != 0 { + min as CUIntMax + } else { + max + }; } - #[test] - fn one() { - let result = unsafe { strtol(b"1\0".as_ptr()) }; - assert_eq!(result, 1); + // Perform negation if requested. + if negate && min == 0 { + num = num.wrapping_neg(); } + // Return a successful result. + num as CUIntMax +} + +#[cfg(feature = "isspace")] +#[no_mangle] +pub unsafe extern "C" fn isspace(c: CInt) -> CInt { + r_isspace(c) as CInt +} + +#[cfg(feature = "isdigit")] +#[no_mangle] +pub unsafe extern "C" fn isdigit(c: CInt) -> CInt { + r_isdigit(c) as CInt +} + +#[cfg(feature = "isalpha")] +#[no_mangle] +pub unsafe extern "C" fn isalpha(c: CInt) -> CInt { + r_isalpha(c) as CInt +} + +#[cfg(feature = "isupper")] +#[no_mangle] +pub unsafe extern "C" fn isupper(c: CInt) -> CInt { + r_isupper(c) as CInt +} + +pub(crate) fn r_isspace(argument: CInt) -> CInt { + match argument as CChar { + b' ' | b'\t' | b'\n' | b'\r' | 0x0b | 0x0c => 1, + _ => 0, + } +} + +pub(crate) fn r_isdigit(argument: CInt) -> CInt { + (b'0'..=b'9').contains(&(argument as CChar)) as CInt +} + +pub(crate) fn r_isalpha(argument: CInt) -> CInt { + let argument = argument as CChar; + ((b'a'..=b'z').contains(&argument) || (b'A'..=b'Z').contains(&argument)) as CInt +} + +pub(crate) fn r_isupper(argument: CInt) -> CInt { + let argument = argument as CChar; + (b'A'..=b'Z').contains(&argument) as CInt +} + +#[cfg(test)] +mod tests { + use core::ptr::null_mut; + + use super::*; + #[test] - fn hundredish() { - let result = unsafe { strtol(b"123\0".as_ptr()) }; - assert_eq!(result, 123); + fn parse_multi_string() { + let string = b"10 200000000000000000000000000000 30 -40\0"; + + let mut s = string.as_ptr() as *mut CChar; + let results = [ + (10, unsafe { s.offset(2) }), + (CULong::MAX, unsafe { s.offset(33) }), + (30, unsafe { s.offset(36) }), + (-40i32 as CULong, unsafe { s.offset(40) }), + ]; + + for (result_number, result_ptr) in results { + let number = unsafe { r_strtoul(s, &mut s as *mut _, 10) }; + + assert_eq!(s, result_ptr); + assert_eq!(number, result_number); + } } #[test] - fn big_long() { - let result = unsafe { strtol(b"2147483647\0".as_ptr()) }; - assert_eq!(result, 2147483647); + fn parse_hex() { + assert_eq!( + unsafe { r_strtoul(b"0xAA123\0".as_ptr(), null_mut(), 0) }, + 0xAA123 + ); + assert_eq!(unsafe { r_strtoul(b"0X00\0".as_ptr(), null_mut(), 0) }, 0x00); + assert_eq!( + unsafe { r_strtoul(b"-0x123456F\0".as_ptr(), null_mut(), 0) }, + (-0x123456Fi32) as _ + ); } } diff --git a/src/strtoul.rs b/src/strtoul.rs deleted file mode 100644 index ba6fc36..0000000 --- a/src/strtoul.rs +++ /dev/null @@ -1,191 +0,0 @@ -//! Copyright (c) 1990 Regents of the University of California. -//! All rights reserved. -//! -//! Redistribution and use in source and binary forms, with or without -//! modification, are permitted provided that the following conditions -//! are met: -//! 1. Redistributions of source code must retain the above copyright -//! notice, this list of conditions and the following disclaimer. -//! 2. Redistributions in binary form must reproduce the above copyright -//! notice, this list of conditions and the following disclaimer in the -//! documentation and/or other materials provided with the distribution. -//! 3. [rescinded 22 July 1999] -//! 4. Neither the name of the University nor the names of its contributors -//! may be used to endorse or promote products derived from this software -//! without specific prior written permission. -//! -//! THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -//! ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -//! IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -//! ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -//! FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -//! DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -//! OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -//! HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -//! LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -//! OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -//! SUCH DAMAGE. -//! -//! Translated from https://github.com/gcc-mirror/gcc/blob/97d1ed67fc6a5773c8c00875bfa3616a457cf5f9/libiberty/strtoul.c - -use crate::{CChar, CInt, CLong, CULong}; - -/// Rust implementation of C library function [`strtoul`](https://cplusplus.com/reference/cstdlib/strtoul/). -/// -/// Passing NULL (core::ptr::null()) gives undefined behaviour. -/// -/// Convert a string to an unsigned long integer. -/// -/// Ignores `locale' stuff. Assumes that the upper and lower case -/// alphabets and digits are each contiguous. -#[no_mangle] -pub unsafe extern "C" fn strtoul( - nptr: *const CChar, - endptr: *mut *const CChar, - mut base: CInt, -) -> CULong { - let mut s = nptr; - - let mut c = *s; - s = s.offset(1); - while isspace(c) { - c = *s; - s = s.offset(1); - } - - let neg = if c == b'-' { - c = *s; - s = s.offset(1); - true - } else { - if c == b'+' { - c = *s; - s = s.offset(1); - } - false - }; - - if (base == 0 || base == 16) && c == b'0' && (*s == b'x' || *s == b'X') { - c = *s.offset(1); - s = s.offset(2); - base = 16; - } - - if base == 0 { - base = if c == b'0' { 8 } else { 10 }; - } - - let cutoff = CULong::MAX / base as CULong; - let cutlim = CULong::MAX % base as CULong; - - let mut acc = 0; - let mut any = 0; - - loop { - if isdigit(c) { - c -= b'0'; - } else if isalpha(c) { - c -= if isupper(c) { b'A' - 10 } else { b'a' - 10 }; - } else { - break; - } - - if c as CInt >= base { - break; - } - - if any < 0 { - c = *s; - s = s.offset(1); - continue; - } - - if acc > cutoff || (acc == cutoff && c as CULong > cutlim) { - any = -1; - acc = CULong::MAX; - } else { - any = 1; - acc *= base as CULong; - acc += c as CULong; - } - - c = *s; - s = s.offset(1); - } - if neg && any > 0 { - acc = -(acc as CLong) as _; - } - - if !endptr.is_null() { - (*endptr) = if any != 0 { - s.offset(-1) - } else { - core::ptr::null() - }; - } - - acc -} - -fn isspace(argument: CChar) -> bool { - // Rust doesn't support "\v" - const VERTICAL_TAB: u8 = 0x0B; - // Rust doesn't support "\f" - const FEED: u8 = 0x0C; - const SPACE_CHARACTERS: [u8; 6] = [b' ', b'\n', b'\t', VERTICAL_TAB, FEED, b'\r']; - - SPACE_CHARACTERS.contains(&argument) -} - -fn isdigit(argument: CChar) -> bool { - (b'0'..=b'9').contains(&argument) -} - -fn isalpha(argument: CChar) -> bool { - (b'a'..=b'z').contains(&argument) || (b'A'..=b'Z').contains(&argument) -} - -fn isupper(argument: CChar) -> bool { - (b'A'..=b'Z').contains(&argument) -} - -#[cfg(test)] -mod tests { - use core::ptr::null_mut; - - use super::*; - - #[test] - fn parse_multi_string() { - let string = b"10 200000000000000000000000000000 30 -40\0"; - - let mut s = string.as_ptr(); - - let results = [ - (10, unsafe { s.offset(2) }), - (CULong::MAX, unsafe { s.offset(33) }), - (30, unsafe { s.offset(36) }), - (-40i32 as CULong, unsafe { s.offset(40) }), - ]; - - for (result_number, result_ptr) in results { - let number = unsafe { strtoul(s, &mut s as *mut _, 10) }; - - assert_eq!(number, result_number); - assert_eq!(s, result_ptr); - } - } - - #[test] - fn parse_hex() { - assert_eq!( - unsafe { strtoul(b"0xAA123\0".as_ptr(), null_mut(), 0) }, - 0xAA123 - ); - assert_eq!(unsafe { strtoul(b"0X00\0".as_ptr(), null_mut(), 0) }, 0x00); - assert_eq!( - unsafe { strtoul(b"-0x123456F\0".as_ptr(), null_mut(), 0) }, - (-0x123456Fi32) as _ - ); - } -} From dcd345358d91f20deb99645d89d7c7f244d9a6c3 Mon Sep 17 00:00:00 2001 From: Myung Gyungmin Date: Wed, 17 Jan 2024 16:10:59 +0900 Subject: [PATCH 03/20] fix Readme --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index e834105..c7ec328 100644 --- a/README.md +++ b/README.md @@ -11,13 +11,21 @@ This crate basically came about so that the [nrfxlib](https://github.com/NordicP * abs * strol * atoi +* isspace +* isdigit +* isalpha +* isupper * strcmp * strncmp * strcpy * strncpy * strlen * strtol +* strtoll * strtoul +* strtoull +* strtoimax +* strtoumax * strstr * strchr * snprintf From b5dade9efb30435037bd8b91a1e06d7a524bcab9 Mon Sep 17 00:00:00 2001 From: Myung Gyungmin Date: Wed, 17 Jan 2024 16:45:45 +0900 Subject: [PATCH 04/20] fix typo --- src/abs.rs | 8 ++----- src/errno.rs | 7 +++--- src/strchr.rs | 2 +- src/strcmp.rs | 26 +++++++++++----------- src/strcpy.rs | 3 +-- src/strlen.rs | 3 +-- src/strncmp.rs | 2 +- src/strncpy.rs | 12 ++++------ src/strstr.rs | 7 ++++-- src/strtol.rs | 59 +++++++++++++++++++++++++++++++++++--------------- 10 files changed, 72 insertions(+), 57 deletions(-) diff --git a/src/abs.rs b/src/abs.rs index e278067..f5afa49 100644 --- a/src/abs.rs +++ b/src/abs.rs @@ -13,15 +13,11 @@ use crate::CInt; #[cfg(feature = "abs")] #[no_mangle] pub extern "C" fn abs(i: CInt) -> CInt { - i.abs() + r_abs(i) } pub(crate) fn r_abs(i: CInt) -> CInt { - if i < 0 { - -i - } else { - i - } + i.abs() } #[cfg(test)] diff --git a/src/errno.rs b/src/errno.rs index 8d8452e..679d1fa 100644 --- a/src/errno.rs +++ b/src/errno.rs @@ -4,17 +4,16 @@ //! Licensed under the Blue Oak Model Licence 1.0.0 #[allow(non_camel_case_types)] - use crate::{CInt, CVoid}; static mut _impure_ptr: *mut CVoid = core::ptr::null_mut(); pub struct Errno { - pub errno: CInt, + pub errno: CInt, } -pub fn errno(errno: CInt) -> Errno{ - Errno { errno } +pub fn errno(errno: CInt) -> Errno { + Errno { errno } } // TODO: implement errno diff --git a/src/strchr.rs b/src/strchr.rs index 0b140c8..8e1afe3 100644 --- a/src/strchr.rs +++ b/src/strchr.rs @@ -9,7 +9,7 @@ use crate::{CChar, CInt}; #[cfg(feature = "strchr")] #[no_mangle] pub unsafe extern "C" fn strchr(haystack: *const CChar, needle: CInt) -> *const CChar { - r_strchr(haystack, needle) + r_strchr(haystack, needle) } pub unsafe fn r_strchr(haystack: *const CChar, needle: CInt) -> *const CChar { diff --git a/src/strcmp.rs b/src/strcmp.rs index 3dec3e7..9e78230 100644 --- a/src/strcmp.rs +++ b/src/strcmp.rs @@ -31,7 +31,7 @@ mod test { #[test] fn test1() { - assert!(unsafe { r_strcmp(b"Hello\0" as *const CChar, b"Hello\0" as *const CChar) } == 0); + assert!(unsafe { r_strcmp(b"Hello\0" as *const CChar, b"Hello\0" as *const CChar) } == 0); } #[test] @@ -39,18 +39,18 @@ mod test { assert!(unsafe { r_strcmp(b"Hello\0" as *const CChar, b"Hello1\0" as *const CChar) } < 0); } - #[test] - fn test3() { - assert!(unsafe { r_strcmp(b"Hello1\0" as *const CChar, b"Hello\0" as *const CChar) } > 0); - } + #[test] + fn test3() { + assert!(unsafe { r_strcmp(b"Hello1\0" as *const CChar, b"Hello\0" as *const CChar) } > 0); + } - #[test] - fn test4() { - assert!(unsafe { r_strcmp(b"\0" as *const CChar, b"Hello\0" as *const CChar) } < 0); - } + #[test] + fn test4() { + assert!(unsafe { r_strcmp(b"\0" as *const CChar, b"Hello\0" as *const CChar) } < 0); + } - #[test] - fn test5() { - assert!(unsafe { r_strcmp(b"Hello\0" as *const CChar, b"\0" as *const CChar) } > 0); - } + #[test] + fn test5() { + assert!(unsafe { r_strcmp(b"Hello\0" as *const CChar, b"\0" as *const CChar) } > 0); + } } diff --git a/src/strcpy.rs b/src/strcpy.rs index 6cc8e09..bf7d0ae 100644 --- a/src/strcpy.rs +++ b/src/strcpy.rs @@ -9,10 +9,9 @@ use crate::CChar; #[cfg(feature = "strcpy")] #[no_mangle] pub unsafe extern "C" fn strcpy(dest: *mut CChar, src: *const CChar) -> *const CChar { - r_strcpy(dest, src) + r_strcpy(dest, src) } - pub unsafe fn r_strcpy(dest: *mut CChar, src: *const CChar) -> *const CChar { let mut i = 0; loop { diff --git a/src/strlen.rs b/src/strlen.rs index 335f4f4..802ac56 100644 --- a/src/strlen.rs +++ b/src/strlen.rs @@ -9,10 +9,9 @@ use crate::CChar; #[cfg(feature = "strlen")] #[no_mangle] pub unsafe extern "C" fn strlen(s: *const CChar) -> usize { - r_strlen(s) + r_strlen(s) } - pub unsafe fn r_strlen(mut s: *const CChar) -> usize { let mut result = 0; while *s != 0 { diff --git a/src/strncmp.rs b/src/strncmp.rs index c7439df..3588bad 100644 --- a/src/strncmp.rs +++ b/src/strncmp.rs @@ -10,7 +10,7 @@ use crate::{CChar, CInt}; #[cfg(feature = "strncmp")] #[no_mangle] pub unsafe extern "C" fn strncmp(s1: *const CChar, s2: *const CChar, n: usize) -> crate::CInt { - r_strncmp(s1, s2, n) + r_strncmp(s1, s2, n) } pub(crate) unsafe fn r_strncmp(s1: *const CChar, s2: *const CChar, n: usize) -> crate::CInt { diff --git a/src/strncpy.rs b/src/strncpy.rs index 0ab980a..a154e80 100644 --- a/src/strncpy.rs +++ b/src/strncpy.rs @@ -10,18 +10,14 @@ use crate::CChar; #[cfg(feature = "strncpy")] #[no_mangle] pub unsafe extern "C" fn strncpy( - dest: *mut CChar, - src: *const CChar, - count: usize, -) -> *const CChar { - r_strncpy(dest, src, count) -} - -pub(crate) unsafe fn r_strncpy( dest: *mut CChar, src: *const CChar, count: usize, ) -> *const CChar { + r_strncpy(dest, src, count) +} + +pub(crate) unsafe fn r_strncpy(dest: *mut CChar, src: *const CChar, count: usize) -> *const CChar { let mut i = 0isize; while i < count as isize { *dest.offset(i) = *src.offset(i); diff --git a/src/strstr.rs b/src/strstr.rs index 29d7243..03fb130 100644 --- a/src/strstr.rs +++ b/src/strstr.rs @@ -8,11 +8,14 @@ use crate::{CChar, CStringIter}; #[cfg(feature = "strstr")] #[no_mangle] pub unsafe extern "C" fn strstr(haystack: *const CChar, needle: *const CChar) -> *const CChar { - r_strstr(haystack, needle) + r_strstr(haystack, needle) } /// Rust implementation of C library function `strstr` -pub(crate) unsafe extern "C" fn r_strstr(haystack: *const CChar, needle: *const CChar) -> *const CChar { +pub(crate) unsafe extern "C" fn r_strstr( + haystack: *const CChar, + needle: *const CChar, +) -> *const CChar { if *needle.offset(0) == 0 { return haystack; } diff --git a/src/strtol.rs b/src/strtol.rs index 52cf37f..2586636 100644 --- a/src/strtol.rs +++ b/src/strtol.rs @@ -1,4 +1,4 @@ -//! Rust implementation of C library function `atof` +//! Rust implementation of C library function `strtol` //! //! Original code from the `c-ward` project. //! Licensed under the MIT license. @@ -8,47 +8,63 @@ use crate::{errno::*, CChar, CInt, CIntMax, CLong, CLongLong, CUIntMax, CULong, #[cfg(feature = "atoi")] #[no_mangle] pub unsafe extern "C" fn atoi(s: *const CChar) -> CInt { - r_atoi(s) + r_atoi(s) } #[cfg(feature = "strtol")] #[no_mangle] pub unsafe extern "C" fn strtol(s: *const CChar, endptr: *mut *mut CChar, base: CInt) -> CLong { - r_strtol(s, endptr, base) + r_strtol(s, endptr, base) } #[cfg(feature = "strtoul")] #[no_mangle] pub unsafe extern "C" fn strtoul(s: *const CChar, endptr: *mut *mut CChar, base: CInt) -> CULong { - r_strtoul(s, endptr, base) + r_strtoul(s, endptr, base) } #[cfg(feature = "strtoll")] #[no_mangle] -pub unsafe extern "C" fn strtoll(s: *const CChar, endptr: *mut *mut CChar, base: CInt) -> CLongLong { - r_strtoll(s, endptr, base) +pub unsafe extern "C" fn strtoll( + s: *const CChar, + endptr: *mut *mut CChar, + base: CInt, +) -> CLongLong { + r_strtoll(s, endptr, base) } #[cfg(feature = "strtoull")] #[no_mangle] -pub unsafe extern "C" fn strtoull(s: *const CChar, endptr: *mut *mut CChar, base: CInt) -> CULongLong { - r_strtoull(s, endptr, base) +pub unsafe extern "C" fn strtoull( + s: *const CChar, + endptr: *mut *mut CChar, + base: CInt, +) -> CULongLong { + r_strtoull(s, endptr, base) } #[cfg(feature = "strtoimax")] #[no_mangle] -pub unsafe extern "C" fn strtoimax(s: *const CChar, endptr: *mut *mut CChar, base: CInt) -> CIntMax { - r_strtoimax(s, endptr, base) +pub unsafe extern "C" fn strtoimax( + s: *const CChar, + endptr: *mut *mut CChar, + base: CInt, +) -> CIntMax { + r_strtoimax(s, endptr, base) } #[cfg(feature = "strtoumax")] #[no_mangle] -pub unsafe extern "C" fn strtoumax(s: *const CChar, endptr: *mut *mut CChar, base: CInt) -> CUIntMax { - r_strtoumax(s, endptr, base) +pub unsafe extern "C" fn strtoumax( + s: *const CChar, + endptr: *mut *mut CChar, + base: CInt, +) -> CUIntMax { + r_strtoumax(s, endptr, base) } pub(crate) unsafe fn r_atoi(s: *const CChar) -> CInt { - r_strtol(s, core::ptr::null_mut(), 10) as CInt + r_strtol(s, core::ptr::null_mut(), 10) as CInt } pub(crate) unsafe fn r_strtol(s: *const CChar, endptr: *mut *mut CChar, base: CInt) -> CLong { @@ -63,7 +79,11 @@ pub(crate) unsafe fn r_strtoll(s: *const CChar, endptr: *mut *mut CChar, base: C r_strtox(s, endptr, base, CLongLong::MIN, CLongLong::MAX as _) as CLongLong } -pub(crate) unsafe fn r_strtoull(s: *const CChar, endptr: *mut *mut CChar, base: CInt) -> CULongLong { +pub(crate) unsafe fn r_strtoull( + s: *const CChar, + endptr: *mut *mut CChar, + base: CInt, +) -> CULongLong { r_strtox(s, endptr, base, 0, CULongLong::MAX) as CULongLong } @@ -72,7 +92,7 @@ pub(crate) unsafe fn r_strtoimax(s: *const CChar, endptr: *mut *mut CChar, base: } pub(crate) unsafe fn r_strtoumax(s: *const CChar, endptr: *mut *mut CChar, base: CInt) -> CUIntMax { - r_strtox(s, endptr, base, 0, CUIntMax::MAX) as CUIntMax + r_strtox(s, endptr, base, 0, CUIntMax::MAX) as CUIntMax } pub(crate) unsafe fn r_strtox( @@ -243,7 +263,7 @@ mod tests { #[test] fn parse_multi_string() { - let string = b"10 200000000000000000000000000000 30 -40\0"; + let string = b"10 200000000000000000000000000000 30 -40\0"; let mut s = string.as_ptr() as *mut CChar; let results = [ @@ -256,7 +276,7 @@ mod tests { for (result_number, result_ptr) in results { let number = unsafe { r_strtoul(s, &mut s as *mut _, 10) }; - assert_eq!(s, result_ptr); + assert_eq!(s, result_ptr); assert_eq!(number, result_number); } } @@ -267,7 +287,10 @@ mod tests { unsafe { r_strtoul(b"0xAA123\0".as_ptr(), null_mut(), 0) }, 0xAA123 ); - assert_eq!(unsafe { r_strtoul(b"0X00\0".as_ptr(), null_mut(), 0) }, 0x00); + assert_eq!( + unsafe { r_strtoul(b"0X00\0".as_ptr(), null_mut(), 0) }, + 0x00 + ); assert_eq!( unsafe { r_strtoul(b"-0x123456F\0".as_ptr(), null_mut(), 0) }, (-0x123456Fi32) as _ From e977b61f4fab5b69362e96f5f6505081595fb734 Mon Sep 17 00:00:00 2001 From: Myung Gyungmin Date: Wed, 17 Jan 2024 16:47:40 +0900 Subject: [PATCH 05/20] cargo fmt --- src/errno.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/errno.rs b/src/errno.rs index 679d1fa..1fa0b2a 100644 --- a/src/errno.rs +++ b/src/errno.rs @@ -6,6 +6,7 @@ #[allow(non_camel_case_types)] use crate::{CInt, CVoid}; +#[allow(non_upper_case_globals)] static mut _impure_ptr: *mut CVoid = core::ptr::null_mut(); pub struct Errno { @@ -17,7 +18,7 @@ pub fn errno(errno: CInt) -> Errno { } // TODO: implement errno -pub fn set_errno(errno: Errno) {} +pub fn set_errno(_errno: Errno) {} pub const EPERM: CInt = 1; pub const ENOENT: CInt = 2; From 9a15a8d28eed89a266431c6ab7be5a93d6d68c92 Mon Sep 17 00:00:00 2001 From: Myung Gyungmin Date: Wed, 17 Jan 2024 22:06:40 +0900 Subject: [PATCH 06/20] fix clippy --- src/errno.rs | 1 - src/lib.rs | 1 + src/strtol.rs | 22 +++++++++------------- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/errno.rs b/src/errno.rs index 1fa0b2a..f84c72f 100644 --- a/src/errno.rs +++ b/src/errno.rs @@ -3,7 +3,6 @@ //! Author: Gyungmin Myung //! Licensed under the Blue Oak Model Licence 1.0.0 -#[allow(non_camel_case_types)] use crate::{CInt, CVoid}; #[allow(non_upper_case_globals)] diff --git a/src/lib.rs b/src/lib.rs index acc5181..d29cc77 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,7 @@ //! See each module for its respective licence. #![cfg_attr(not(test), no_std)] +#![allow(clippy::missing_safety_doc)] #[cfg(test)] #[allow(unused_imports)] diff --git a/src/strtol.rs b/src/strtol.rs index 2586636..7a1c92b 100644 --- a/src/strtol.rs +++ b/src/strtol.rs @@ -102,7 +102,7 @@ pub(crate) unsafe fn r_strtox( min: CIntMax, max: CUIntMax, ) -> CUIntMax { - if base < 0 || base > 36 { + if !(0..=36).contains(&base) { set_errno(errno(EINVAL)); return 0; } @@ -127,7 +127,7 @@ pub(crate) unsafe fn r_strtox( if base == 0 { if *s == b'0' as CChar { s = s.add(1); - if (*s == b'x' as CChar || *s == b'X' as CChar) && (*s.add(1) as u8).is_ascii_hexdigit() + if (*s == b'x' as CChar || *s == b'X' as CChar) && (*s.add(1)).is_ascii_hexdigit() { s = s.add(1); base = 16; @@ -140,7 +140,7 @@ pub(crate) unsafe fn r_strtox( } else if base == 16 && *s == b'0' as CChar && (*s.add(1) == b'x' as CChar || *s.add(1) == b'X' as CChar) - && (*s.add(2) as u8).is_ascii_hexdigit() + && (*s.add(2)).is_ascii_hexdigit() { s = s.add(2); } @@ -149,7 +149,7 @@ pub(crate) unsafe fn r_strtox( let mut overflow = false; let mut num: CUIntMax = 0; loop { - let digit: CUIntMax = match *s as u8 { + let digit: CUIntMax = match *s { x @ b'0'..=b'9' => x - b'0', x @ b'a'..=b'z' => x - b'a' + 10, x @ b'A'..=b'Z' => x - b'A' + 10, @@ -164,10 +164,8 @@ pub(crate) unsafe fn r_strtox( if (num as CIntMax) < min / base as CIntMax { overflow = true; } - } else { - if num > max / base { - overflow = true; - } + } else if num > max / base { + overflow = true; } num = num.wrapping_mul(base); @@ -242,17 +240,15 @@ pub(crate) fn r_isspace(argument: CInt) -> CInt { } pub(crate) fn r_isdigit(argument: CInt) -> CInt { - (b'0'..=b'9').contains(&(argument as CChar)) as CInt + (argument as CChar).is_ascii_digit() as CInt } pub(crate) fn r_isalpha(argument: CInt) -> CInt { - let argument = argument as CChar; - ((b'a'..=b'z').contains(&argument) || (b'A'..=b'Z').contains(&argument)) as CInt + (argument as CChar).is_ascii_alphabetic() as CInt } pub(crate) fn r_isupper(argument: CInt) -> CInt { - let argument = argument as CChar; - (b'A'..=b'Z').contains(&argument) as CInt + (argument as CChar).is_ascii_uppercase() as CInt } #[cfg(test)] From cabad4af7c74fa8de8ab05f8a562d4399445ec25 Mon Sep 17 00:00:00 2001 From: Myung Gyungmin Date: Wed, 17 Jan 2024 22:11:40 +0900 Subject: [PATCH 07/20] fix type casting --- src/strtol.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/strtol.rs b/src/strtol.rs index 7a1c92b..e5230eb 100644 --- a/src/strtol.rs +++ b/src/strtol.rs @@ -68,11 +68,11 @@ pub(crate) unsafe fn r_atoi(s: *const CChar) -> CInt { } pub(crate) unsafe fn r_strtol(s: *const CChar, endptr: *mut *mut CChar, base: CInt) -> CLong { - r_strtox(s, endptr, base, CLong::MIN, CLong::MAX as _) as CLong + r_strtox(s, endptr, base, CLong::MIN as _, CLong::MAX as _) as CLong } pub(crate) unsafe fn r_strtoul(s: *const CChar, endptr: *mut *mut CChar, base: CInt) -> CULong { - r_strtox(s, endptr, base, 0, CULong::MAX) as CULong + r_strtox(s, endptr, base, 0, CULong::MAX as _) as CULong } pub(crate) unsafe fn r_strtoll(s: *const CChar, endptr: *mut *mut CChar, base: CInt) -> CLongLong { @@ -127,8 +127,7 @@ pub(crate) unsafe fn r_strtox( if base == 0 { if *s == b'0' as CChar { s = s.add(1); - if (*s == b'x' as CChar || *s == b'X' as CChar) && (*s.add(1)).is_ascii_hexdigit() - { + if (*s == b'x' as CChar || *s == b'X' as CChar) && (*s.add(1)).is_ascii_hexdigit() { s = s.add(1); base = 16; } else { @@ -240,15 +239,15 @@ pub(crate) fn r_isspace(argument: CInt) -> CInt { } pub(crate) fn r_isdigit(argument: CInt) -> CInt { - (argument as CChar).is_ascii_digit() as CInt + (argument as CChar).is_ascii_digit() as CInt } pub(crate) fn r_isalpha(argument: CInt) -> CInt { - (argument as CChar).is_ascii_alphabetic() as CInt + (argument as CChar).is_ascii_alphabetic() as CInt } pub(crate) fn r_isupper(argument: CInt) -> CInt { - (argument as CChar).is_ascii_uppercase() as CInt + (argument as CChar).is_ascii_uppercase() as CInt } #[cfg(test)] From 55e01205e7a2d7922215d5a055ddb1fd2509da40 Mon Sep 17 00:00:00 2001 From: Myung Gyungmin Date: Fri, 19 Jan 2024 22:53:23 +0900 Subject: [PATCH 08/20] update clippy version --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 59e1c61..c7601e3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -103,7 +103,7 @@ jobs: - uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: 1.64.0 # clippy is too much of a moving target at the moment + toolchain: 1.77.0 # clippy is too much of a moving target at the moment override: true components: clippy - uses: actions-rs/clippy-check@v1 From 0e65d88bb0b6da5c9b5af2505af166147404bb9a Mon Sep 17 00:00:00 2001 From: Myung Gyungmin Date: Fri, 19 Jan 2024 22:55:29 +0900 Subject: [PATCH 09/20] update clippy version to 1.75.0 --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c7601e3..bd71834 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -103,7 +103,7 @@ jobs: - uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: 1.77.0 # clippy is too much of a moving target at the moment + toolchain: 1.75.0 # clippy is too much of a moving target at the moment override: true components: clippy - uses: actions-rs/clippy-check@v1 From 3af14f507031886fc87e6c9cfdd9aa3a27e16a59 Mon Sep 17 00:00:00 2001 From: Myung Gyungmin Date: Sat, 20 Jan 2024 10:56:20 +0900 Subject: [PATCH 10/20] intermediate change --- build.rs | 3 +++ src/strtol.rs | 30 +++++++++++++++--------------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/build.rs b/build.rs index 46130ba..d3dee7b 100644 --- a/build.rs +++ b/build.rs @@ -8,4 +8,7 @@ fn main() { .file("./src/snprintf.c") .compile("clocal"); } + + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=src/snprintf.c"); } diff --git a/src/strtol.rs b/src/strtol.rs index e5230eb..377c258 100644 --- a/src/strtol.rs +++ b/src/strtol.rs @@ -13,13 +13,13 @@ pub unsafe extern "C" fn atoi(s: *const CChar) -> CInt { #[cfg(feature = "strtol")] #[no_mangle] -pub unsafe extern "C" fn strtol(s: *const CChar, endptr: *mut *mut CChar, base: CInt) -> CLong { +pub unsafe extern "C" fn strtol(s: *const CChar, endptr: *mut *const CChar, base: CInt) -> CLong { r_strtol(s, endptr, base) } #[cfg(feature = "strtoul")] -#[no_mangle] -pub unsafe extern "C" fn strtoul(s: *const CChar, endptr: *mut *mut CChar, base: CInt) -> CULong { +//#[no_mangle] +pub unsafe extern "C" fn strtoul(s: *const CChar, endptr: *mut *const CChar, base: CInt) -> CULong { r_strtoul(s, endptr, base) } @@ -27,7 +27,7 @@ pub unsafe extern "C" fn strtoul(s: *const CChar, endptr: *mut *mut CChar, base: #[no_mangle] pub unsafe extern "C" fn strtoll( s: *const CChar, - endptr: *mut *mut CChar, + endptr: *mut *const CChar, base: CInt, ) -> CLongLong { r_strtoll(s, endptr, base) @@ -37,7 +37,7 @@ pub unsafe extern "C" fn strtoll( #[no_mangle] pub unsafe extern "C" fn strtoull( s: *const CChar, - endptr: *mut *mut CChar, + endptr: *mut *const CChar, base: CInt, ) -> CULongLong { r_strtoull(s, endptr, base) @@ -47,7 +47,7 @@ pub unsafe extern "C" fn strtoull( #[no_mangle] pub unsafe extern "C" fn strtoimax( s: *const CChar, - endptr: *mut *mut CChar, + endptr: *mut *const CChar, base: CInt, ) -> CIntMax { r_strtoimax(s, endptr, base) @@ -57,7 +57,7 @@ pub unsafe extern "C" fn strtoimax( #[no_mangle] pub unsafe extern "C" fn strtoumax( s: *const CChar, - endptr: *mut *mut CChar, + endptr: *mut *const CChar, base: CInt, ) -> CUIntMax { r_strtoumax(s, endptr, base) @@ -67,37 +67,37 @@ pub(crate) unsafe fn r_atoi(s: *const CChar) -> CInt { r_strtol(s, core::ptr::null_mut(), 10) as CInt } -pub(crate) unsafe fn r_strtol(s: *const CChar, endptr: *mut *mut CChar, base: CInt) -> CLong { +pub(crate) unsafe fn r_strtol(s: *const CChar, endptr: *mut *const CChar, base: CInt) -> CLong { r_strtox(s, endptr, base, CLong::MIN as _, CLong::MAX as _) as CLong } -pub(crate) unsafe fn r_strtoul(s: *const CChar, endptr: *mut *mut CChar, base: CInt) -> CULong { +pub(crate) unsafe fn r_strtoul(s: *const CChar, endptr: *mut *const CChar, base: CInt) -> CULong { r_strtox(s, endptr, base, 0, CULong::MAX as _) as CULong } -pub(crate) unsafe fn r_strtoll(s: *const CChar, endptr: *mut *mut CChar, base: CInt) -> CLongLong { +pub(crate) unsafe fn r_strtoll(s: *const CChar, endptr: *mut *const CChar, base: CInt) -> CLongLong { r_strtox(s, endptr, base, CLongLong::MIN, CLongLong::MAX as _) as CLongLong } pub(crate) unsafe fn r_strtoull( s: *const CChar, - endptr: *mut *mut CChar, + endptr: *mut *const CChar, base: CInt, ) -> CULongLong { r_strtox(s, endptr, base, 0, CULongLong::MAX) as CULongLong } -pub(crate) unsafe fn r_strtoimax(s: *const CChar, endptr: *mut *mut CChar, base: CInt) -> CIntMax { +pub(crate) unsafe fn r_strtoimax(s: *const CChar, endptr: *mut *const CChar, base: CInt) -> CIntMax { r_strtox(s, endptr, base, CIntMax::MIN, CIntMax::MAX as _) as CIntMax } -pub(crate) unsafe fn r_strtoumax(s: *const CChar, endptr: *mut *mut CChar, base: CInt) -> CUIntMax { +pub(crate) unsafe fn r_strtoumax(s: *const CChar, endptr: *mut *const CChar, base: CInt) -> CUIntMax { r_strtox(s, endptr, base, 0, CUIntMax::MAX) as CUIntMax } pub(crate) unsafe fn r_strtox( s: *const CChar, - endptr: *mut *mut CChar, + endptr: *mut *const CChar, base: CInt, min: CIntMax, max: CUIntMax, @@ -260,7 +260,7 @@ mod tests { fn parse_multi_string() { let string = b"10 200000000000000000000000000000 30 -40\0"; - let mut s = string.as_ptr() as *mut CChar; + let mut s = string.as_ptr(); let results = [ (10, unsafe { s.offset(2) }), (CULong::MAX, unsafe { s.offset(33) }), From 09766e98e5e15e745c68254a9b11681a3d5497b6 Mon Sep 17 00:00:00 2001 From: Myung Gyungmin Date: Sat, 20 Jan 2024 11:57:26 +0900 Subject: [PATCH 11/20] fix c lint error --- src/cn | 0 src/snprintf.c | 4 +++- 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 src/cn diff --git a/src/cn b/src/cn new file mode 100644 index 0000000..e69de29 diff --git a/src/snprintf.c b/src/snprintf.c index 8c88091..6d9c060 100644 --- a/src/snprintf.c +++ b/src/snprintf.c @@ -65,7 +65,7 @@ extern int32_t utoa(uint64_t i, char* s, size_t s_len, uint8_t radix); /** * This is provided by `strtoul.rs`. It converts a string to a long. */ -extern unsigned long int strtoul(const char* str, const char* restrict* endptr, int base); +extern unsigned long int strtoul(const char* str, char** endptr, int base); /* ======================================================================== * * @@ -341,6 +341,8 @@ int vsnprintf( // Next up is either a number or a '*' that signifies that the number is in the arguments list char next = *++fmt; + char* fmt = fmt; + if (next == '*') { precision = va_arg( ap, int ); From 01a4b0f047cef68cd6b145c6e92d8a96081c4163 Mon Sep 17 00:00:00 2001 From: Myung Gyungmin Date: Sat, 20 Jan 2024 15:54:19 +0900 Subject: [PATCH 12/20] fix c lint error 2 --- src/snprintf.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/snprintf.c b/src/snprintf.c index 6d9c060..9a06757 100644 --- a/src/snprintf.c +++ b/src/snprintf.c @@ -106,7 +106,7 @@ extern unsigned long int strtoul(const char* str, char** endptr, int base); * @return the number of characters written to `str` */ int vsnprintf( - char* restrict str, size_t size, const char* restrict fmt, va_list ap ) + char* restrict str, size_t size, const char* fmt, va_list ap ) { size_t written = 0; bool is_escape = false; @@ -341,15 +341,13 @@ int vsnprintf( // Next up is either a number or a '*' that signifies that the number is in the arguments list char next = *++fmt; - char* fmt = fmt; - if (next == '*') { precision = va_arg( ap, int ); } else { - precision = strtoul(fmt, &fmt, 10); + precision = strtoul(fmt, (char**) &fmt, 10); // Strtoul sets the fmt pointer to the char after the number, // however the code expects the char before that. fmt--; From 52c91a4172aaffaa4bd8de3283cef1e33b51295b Mon Sep 17 00:00:00 2001 From: Myung Gyungmin Date: Sat, 20 Jan 2024 16:52:43 +0900 Subject: [PATCH 13/20] use cfg_attr --- src/abs.rs | 17 ++---- src/cn | 0 src/errno.rs | 154 ------------------------------------------------ src/itoa.rs | 6 +- src/lib.rs | 53 ++++++++++++----- src/strchr.rs | 19 +++--- src/strcmp.rs | 19 +++--- src/strcpy.rs | 13 ++--- src/strlen.rs | 15 ++--- src/strncmp.rs | 15 ++--- src/strncpy.rs | 17 ++---- src/strstr.rs | 18 +++--- src/strtol.rs | 156 +++++++++++++++---------------------------------- 13 files changed, 135 insertions(+), 367 deletions(-) delete mode 100644 src/cn delete mode 100644 src/errno.rs diff --git a/src/abs.rs b/src/abs.rs index f5afa49..e89befc 100644 --- a/src/abs.rs +++ b/src/abs.rs @@ -4,19 +4,14 @@ use crate::CInt; -/// Calculates the integer absolute value +/// Rust implementation of C library function `abs` /// /// ``` /// use tinyrlibc::abs; /// assert_eq!(abs(-2), 2); /// ``` -#[cfg(feature = "abs")] -#[no_mangle] -pub extern "C" fn abs(i: CInt) -> CInt { - r_abs(i) -} - -pub(crate) fn r_abs(i: CInt) -> CInt { +#[cfg_attr(feature = "abs", export_name = "abs")] +pub fn abs(i: CInt) -> CInt { i.abs() } @@ -26,16 +21,16 @@ mod test { #[test] fn neg() { - assert_eq!(r_abs(-2), 2); + assert_eq!(abs(-2), 2); } #[test] fn pos() { - assert_eq!(r_abs(3), 3); + assert_eq!(abs(3), 3); } #[test] fn zero() { - assert_eq!(r_abs(0), 0); + assert_eq!(abs(0), 0); } } diff --git a/src/cn b/src/cn deleted file mode 100644 index e69de29..0000000 diff --git a/src/errno.rs b/src/errno.rs deleted file mode 100644 index f84c72f..0000000 --- a/src/errno.rs +++ /dev/null @@ -1,154 +0,0 @@ -//! Rust implementation of C library function `errno` -//! -//! Author: Gyungmin Myung -//! Licensed under the Blue Oak Model Licence 1.0.0 - -use crate::{CInt, CVoid}; - -#[allow(non_upper_case_globals)] -static mut _impure_ptr: *mut CVoid = core::ptr::null_mut(); - -pub struct Errno { - pub errno: CInt, -} - -pub fn errno(errno: CInt) -> Errno { - Errno { errno } -} - -// TODO: implement errno -pub fn set_errno(_errno: Errno) {} - -pub const EPERM: CInt = 1; -pub const ENOENT: CInt = 2; -pub const ESRCH: CInt = 3; -pub const EINTR: CInt = 4; -pub const EIO: CInt = 5; -pub const ENXIO: CInt = 6; -pub const E2BIG: CInt = 7; -pub const ENOEXEC: CInt = 8; -pub const EBADF: CInt = 9; -pub const ECHILD: CInt = 10; -pub const EAGAIN: CInt = 11; -pub const ENOMEM: CInt = 12; -pub const EACCES: CInt = 13; -pub const EFAULT: CInt = 14; -pub const ENOTBLK: CInt = 15; -pub const EBUSY: CInt = 16; -pub const EEXIST: CInt = 17; -pub const EXDEV: CInt = 18; -pub const ENODEV: CInt = 19; -pub const ENOTDIR: CInt = 20; -pub const EISDIR: CInt = 21; -pub const EINVAL: CInt = 22; -pub const ENFILE: CInt = 23; -pub const EMFILE: CInt = 24; -pub const ENOTTY: CInt = 25; -pub const ETXTBSY: CInt = 26; -pub const EFBIG: CInt = 27; -pub const ENOSPC: CInt = 28; -pub const ESPIPE: CInt = 29; -pub const EROFS: CInt = 30; -pub const EMLINK: CInt = 31; -pub const EPIPE: CInt = 32; -pub const EDOM: CInt = 33; -pub const ERANGE: CInt = 34; -pub const EDEADLK: CInt = 35; -pub const ENAMETOOLONG: CInt = 36; -pub const ENOLCK: CInt = 37; -pub const ENOSYS: CInt = 38; -pub const ENOTEMPTY: CInt = 39; -pub const ELOOP: CInt = 40; -pub const EWOULDBLOCK: CInt = EAGAIN; -pub const ENOMSG: CInt = 42; -pub const EIDRM: CInt = 43; -pub const ECHRNG: CInt = 44; -pub const EL2NSYNC: CInt = 45; -pub const EL3HLT: CInt = 46; -pub const EL3RST: CInt = 47; -pub const ELNRNG: CInt = 48; -pub const EUNATCH: CInt = 49; -pub const ENOCSI: CInt = 50; -pub const EL2HLT: CInt = 51; -pub const EBADE: CInt = 52; -pub const EBADR: CInt = 53; -pub const EXFULL: CInt = 54; -pub const ENOANO: CInt = 55; -pub const EBADRQC: CInt = 56; -pub const EBADSLT: CInt = 57; -pub const EDEADLOCK: CInt = EDEADLK; -pub const EBFONT: CInt = 59; -pub const ENOSTR: CInt = 60; -pub const ENODATA: CInt = 61; -pub const ETIME: CInt = 62; -pub const ENOSR: CInt = 63; -pub const ENONET: CInt = 64; -pub const ENOPKG: CInt = 65; -pub const EREMOTE: CInt = 66; -pub const ENOLINK: CInt = 67; -pub const EADV: CInt = 68; -pub const ESRMNT: CInt = 69; -pub const ECOMM: CInt = 70; -pub const EPROTO: CInt = 71; -pub const EMULTIHOP: CInt = 72; -pub const EDOTDOT: CInt = 73; -pub const EBADMSG: CInt = 74; -pub const EOVERFLOW: CInt = 75; -pub const ENOTUNIQ: CInt = 76; -pub const EBADFD: CInt = 77; -pub const EREMCHG: CInt = 78; -pub const ELIBACC: CInt = 79; -pub const ELIBBAD: CInt = 80; -pub const ELIBSCN: CInt = 81; -pub const ELIBMAX: CInt = 82; -pub const ELIBEXEC: CInt = 83; -pub const EILSEQ: CInt = 84; -pub const ERESTART: CInt = 85; -pub const ESTRPIPE: CInt = 86; -pub const EUSERS: CInt = 87; -pub const ENOTSOCK: CInt = 88; -pub const EDESTADDRREQ: CInt = 89; -pub const EMSGSIZE: CInt = 90; -pub const EPROTOTYPE: CInt = 91; -pub const ENOPROTOOPT: CInt = 92; -pub const EPROTONOSUPPORT: CInt = 93; -pub const ESOCKTNOSUPPORT: CInt = 94; -pub const EOPNOTSUPP: CInt = 95; -pub const ENOTSUP: CInt = EOPNOTSUPP; -pub const EPFNOSUPPORT: CInt = 96; -pub const EAFNOSUPPORT: CInt = 97; -pub const EADDRINUSE: CInt = 98; -pub const EADDRNOTAVAIL: CInt = 99; -pub const ENETDOWN: CInt = 100; -pub const ENETUNREACH: CInt = 101; -pub const ENETRESET: CInt = 102; -pub const ECONNABORTED: CInt = 103; -pub const ECONNRESET: CInt = 104; -pub const ENOBUFS: CInt = 105; -pub const EISCONN: CInt = 106; -pub const ENOTCONN: CInt = 107; -pub const ESHUTDOWN: CInt = 108; -pub const ETOOMANYREFS: CInt = 109; -pub const ETIMEDOUT: CInt = 110; -pub const ECONNREFUSED: CInt = 111; -pub const EHOSTDOWN: CInt = 112; -pub const EHOSTUNREACH: CInt = 113; -pub const EALREADY: CInt = 114; -pub const EINPROGRESS: CInt = 115; -pub const ESTALE: CInt = 116; -pub const EUCLEAN: CInt = 117; -pub const ENOTNAM: CInt = 118; -pub const ENAVAIL: CInt = 119; -pub const EISNAM: CInt = 120; -pub const EREMOTEIO: CInt = 121; -pub const EDQUOT: CInt = 122; -pub const ENOMEDIUM: CInt = 123; -pub const EMEDIUMTYPE: CInt = 124; -pub const ECANCELED: CInt = 125; -pub const ENOKEY: CInt = 126; -pub const EKEYEXPIRED: CInt = 127; -pub const EKEYREVOKED: CInt = 128; -pub const EKEYREJECTED: CInt = 129; -pub const EOWNERDEAD: CInt = 130; -pub const ENOTRECOVERABLE: CInt = 131; -pub const ERFKILL: CInt = 132; diff --git a/src/itoa.rs b/src/itoa.rs index ed3dc94..892dbce 100644 --- a/src/itoa.rs +++ b/src/itoa.rs @@ -8,7 +8,6 @@ use crate::CChar; -#[no_mangle] /// Formats the given value `i`, with the given radix, into the given buffer (of the given length). /// /// No prefixes (like 0x or 0b) are generated. Only radix values in the range @@ -16,6 +15,7 @@ use crate::CChar; /// /// Returns the number of bytes written on success (not including the null), /// or -1 if the buffer wasn't large enough. +#[cfg_attr(feature = "itoa", no_mangle)] pub unsafe extern "C" fn itoa(i: i64, s: *mut CChar, s_len: usize, radix: u8) -> i32 { let (is_negative, pos_i) = if i < 0 { (true, (-i) as u64) @@ -31,7 +31,6 @@ pub unsafe extern "C" fn itoa(i: i64, s: *mut CChar, s_len: usize, radix: u8) -> } } -#[no_mangle] /// Formats the given value `u`, with the given radix, into the given buffer (of the given length). /// /// No prefixes (like 0x or 0b) are generated. Only radix values in the range @@ -39,7 +38,8 @@ pub unsafe extern "C" fn itoa(i: i64, s: *mut CChar, s_len: usize, radix: u8) -> /// /// Returns the number of bytes written on success (not including the null), /// or -1 if the buffer wasn't large enough. -pub unsafe extern "C" fn utoa(mut u: u64, s: *mut CChar, s_len: usize, radix: u8) -> i32 { +#[cfg_attr(feature = "itoa", no_mangle)] +unsafe extern "C" fn utoa(mut u: u64, s: *mut CChar, s_len: usize, radix: u8) -> i32 { let buffer_slice = core::slice::from_raw_parts_mut(s, s_len); // Build the number up in buffer s in reverse order diff --git a/src/lib.rs b/src/lib.rs index d29cc77..5e37ca7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,7 @@ //! This file is Copyright (c) Jonathan 'theJPster' Pallant 2019 //! Licensed under the Blue Oak Model Licence 1.0.0 //! -//! See each module for its respective licence. +//! See each module for its respective license. #![cfg_attr(not(test), no_std)] #![allow(clippy::missing_safety_doc)] @@ -14,38 +14,63 @@ #[allow(unused_imports)] use std as core; -mod errno; -pub use self::errno::*; - mod itoa; -pub use self::itoa::*; mod abs; -pub use self::abs::*; +#[cfg(feature = "abs")] +pub use self::abs::abs; mod strcmp; -pub use self::strcmp::*; +#[cfg(feature = "strcmp")] +pub use self::strcmp::strcmp; mod strncmp; -pub use self::strncmp::*; +#[cfg(feature = "strncmp")] +pub use self::strncmp::strncmp; mod strcpy; -pub use self::strcpy::*; +#[cfg(feature = "strcpy")] +pub use self::strcpy::strcpy; mod strncpy; -pub use self::strncpy::*; +#[cfg(feature = "strncpy")] +pub use self::strncpy::strncpy; mod strlen; -pub use self::strlen::*; +#[cfg(feature = "strlen")] +pub use self::strlen::strlen; mod strtol; -pub use self::strtol::*; +#[cfg(feature = "atoi")] +pub use self::strtol::atoi; +#[cfg(feature = "strtol")] +pub use self::strtol::strtol; +#[cfg(feature = "strtoul")] +pub use self::strtol::strtoul; +#[cfg(feature = "strtoll")] +pub use self::strtol::strtoll; +#[cfg(feature = "strtoull")] +pub use self::strtol::strtoull; +#[cfg(feature = "strtoimax")] +pub use self::strtol::strtoimax; +#[cfg(feature = "strtoumax")] +pub use self::strtol::strtoumax; +#[cfg(feature = "isspace")] +pub use self::strtol::isspace; +#[cfg(feature = "isalpha")] +pub use self::strtol::isalpha; +#[cfg(feature = "isdigit")] +pub use self::strtol::isdigit; +#[cfg(feature = "isupper")] +pub use self::strtol::isupper; mod strstr; -pub use self::strstr::*; +#[cfg(feature = "strstr")] +pub use self::strstr::strstr; mod strchr; -pub use self::strchr::*; +#[cfg(feature = "strchr")] +pub use self::strchr::strchr; mod snprintf; diff --git a/src/strchr.rs b/src/strchr.rs index 8e1afe3..de9113c 100644 --- a/src/strchr.rs +++ b/src/strchr.rs @@ -6,13 +6,8 @@ use crate::{CChar, CInt}; /// Rust implementation of C library function `strchr` -#[cfg(feature = "strchr")] -#[no_mangle] -pub unsafe extern "C" fn strchr(haystack: *const CChar, needle: CInt) -> *const CChar { - r_strchr(haystack, needle) -} - -pub unsafe fn r_strchr(haystack: *const CChar, needle: CInt) -> *const CChar { +#[cfg_attr(feature = "strchr", export_name = "strchr")] +pub unsafe fn strchr(haystack: *const CChar, needle: CInt) -> *const CChar { for idx in 0.. { let ptr = haystack.offset(idx); if needle == (*ptr) as CInt { @@ -32,35 +27,35 @@ mod test { #[test] fn no_match() { let haystack = b"haystack\0".as_ptr(); - let result = unsafe { r_strchr(haystack, b'X' as CInt) }; + let result = unsafe { strchr(haystack, b'X' as CInt) }; assert_eq!(result, core::ptr::null()); } #[test] fn null() { let haystack = b"haystack\0".as_ptr(); - let result = unsafe { r_strchr(haystack, 0) }; + let result = unsafe { strchr(haystack, 0) }; assert_eq!(result, unsafe { haystack.offset(8) }); } #[test] fn start() { let haystack = b"haystack\0".as_ptr(); - let result = unsafe { r_strchr(haystack, b'h' as CInt) }; + let result = unsafe { strchr(haystack, b'h' as CInt) }; assert_eq!(result, haystack); } #[test] fn middle() { let haystack = b"haystack\0".as_ptr(); - let result = unsafe { r_strchr(haystack, b'y' as CInt) }; + let result = unsafe { strchr(haystack, b'y' as CInt) }; assert_eq!(result, unsafe { haystack.offset(2) }); } #[test] fn end() { let haystack = b"haystack\0".as_ptr(); - let result = unsafe { r_strchr(haystack, b'k' as CInt) }; + let result = unsafe { strchr(haystack, b'k' as CInt) }; assert_eq!(result, unsafe { haystack.offset(7) }); } } diff --git a/src/strcmp.rs b/src/strcmp.rs index 9e78230..c147506 100644 --- a/src/strcmp.rs +++ b/src/strcmp.rs @@ -6,13 +6,8 @@ use crate::{CChar, CInt}; /// Rust implementation of C library function `strcmp` -#[no_mangle] -#[cfg(feature = "strcmp")] -pub unsafe extern "C" fn strcmp(s1: *const CChar, s2: *const CChar) -> CInt { - r_strcmp(s1, s2) -} - -pub unsafe fn r_strcmp(s1: *const CChar, s2: *const CChar) -> CInt { +#[cfg_attr(feature = "strcmp", export_name = "strcmp")] +pub unsafe fn strcmp(s1: *const CChar, s2: *const CChar) -> CInt { for i in 0.. { let s1_i = s1.offset(i); let s2_i = s2.offset(i); @@ -31,26 +26,26 @@ mod test { #[test] fn test1() { - assert!(unsafe { r_strcmp(b"Hello\0" as *const CChar, b"Hello\0" as *const CChar) } == 0); + assert!(unsafe { strcmp(b"Hello\0" as *const CChar, b"Hello\0" as *const CChar) } == 0); } #[test] fn test2() { - assert!(unsafe { r_strcmp(b"Hello\0" as *const CChar, b"Hello1\0" as *const CChar) } < 0); + assert!(unsafe { strcmp(b"Hello\0" as *const CChar, b"Hello1\0" as *const CChar) } < 0); } #[test] fn test3() { - assert!(unsafe { r_strcmp(b"Hello1\0" as *const CChar, b"Hello\0" as *const CChar) } > 0); + assert!(unsafe { strcmp(b"Hello1\0" as *const CChar, b"Hello\0" as *const CChar) } > 0); } #[test] fn test4() { - assert!(unsafe { r_strcmp(b"\0" as *const CChar, b"Hello\0" as *const CChar) } < 0); + assert!(unsafe { strcmp(b"\0" as *const CChar, b"Hello\0" as *const CChar) } < 0); } #[test] fn test5() { - assert!(unsafe { r_strcmp(b"Hello\0" as *const CChar, b"\0" as *const CChar) } > 0); + assert!(unsafe { strcmp(b"Hello\0" as *const CChar, b"\0" as *const CChar) } > 0); } } diff --git a/src/strcpy.rs b/src/strcpy.rs index bf7d0ae..2fc03c9 100644 --- a/src/strcpy.rs +++ b/src/strcpy.rs @@ -6,13 +6,8 @@ use crate::CChar; /// Rust implementation of C library function `strcpy`. Passing NULL /// (core::ptr::null()) gives undefined behaviour. -#[cfg(feature = "strcpy")] -#[no_mangle] -pub unsafe extern "C" fn strcpy(dest: *mut CChar, src: *const CChar) -> *const CChar { - r_strcpy(dest, src) -} - -pub unsafe fn r_strcpy(dest: *mut CChar, src: *const CChar) -> *const CChar { +#[cfg_attr(feature = "strcpy", export_name = "strcpy")] +pub unsafe fn strcpy(dest: *mut CChar, src: *const CChar) -> *const CChar { let mut i = 0; loop { *dest.offset(i) = *src.offset(i); @@ -33,7 +28,7 @@ mod test { fn short() { let src = b"hi\0"; let mut dest = *b"abcdef"; // no null terminator - let result = unsafe { r_strcpy(dest.as_mut_ptr(), src.as_ptr()) }; + let result = unsafe { strcpy(dest.as_mut_ptr(), src.as_ptr()) }; assert_eq!( unsafe { core::slice::from_raw_parts(result, 5) }, *b"hi\0de" @@ -44,7 +39,7 @@ mod test { fn two() { let src = b"hi\0"; let mut dest = [0u8; 2]; // no space for null terminator - let result = unsafe { r_strcpy(dest.as_mut_ptr(), src.as_ptr()) }; + let result = unsafe { strcpy(dest.as_mut_ptr(), src.as_ptr()) }; assert_eq!(unsafe { core::slice::from_raw_parts(result, 2) }, b"hi"); } } diff --git a/src/strlen.rs b/src/strlen.rs index 802ac56..2a1b7c1 100644 --- a/src/strlen.rs +++ b/src/strlen.rs @@ -6,13 +6,8 @@ use crate::CChar; /// Rust implementation of C library function `strlen` -#[cfg(feature = "strlen")] -#[no_mangle] -pub unsafe extern "C" fn strlen(s: *const CChar) -> usize { - r_strlen(s) -} - -pub unsafe fn r_strlen(mut s: *const CChar) -> usize { +#[cfg_attr(feature = "strlen", export_name = "strlen")] +pub unsafe fn strlen(mut s: *const CChar) -> usize { let mut result = 0; while *s != 0 { s = s.offset(1); @@ -27,16 +22,16 @@ mod test { #[test] fn test1() { - assert_eq!(unsafe { r_strlen(b"Hello\0" as *const CChar) }, 5); + assert_eq!(unsafe { strlen(b"Hello\0" as *const CChar) }, 5); } #[test] fn test2() { - assert_eq!(unsafe { r_strlen(b"\0" as *const CChar) }, 0); + assert_eq!(unsafe { strlen(b"\0" as *const CChar) }, 0); } #[test] fn test3() { - assert_eq!(unsafe { r_strlen(b"X\0" as *const CChar) }, 1); + assert_eq!(unsafe { strlen(b"X\0" as *const CChar) }, 1); } } diff --git a/src/strncmp.rs b/src/strncmp.rs index 3588bad..dbd45c4 100644 --- a/src/strncmp.rs +++ b/src/strncmp.rs @@ -7,13 +7,8 @@ use crate::{CChar, CInt}; /// Rust implementation of C library function `strncmp`. Passing NULL /// (core::ptr::null()) gives undefined behaviour. -#[cfg(feature = "strncmp")] -#[no_mangle] -pub unsafe extern "C" fn strncmp(s1: *const CChar, s2: *const CChar, n: usize) -> crate::CInt { - r_strncmp(s1, s2, n) -} - -pub(crate) unsafe fn r_strncmp(s1: *const CChar, s2: *const CChar, n: usize) -> crate::CInt { +#[cfg_attr(feature = "strncmp", export_name = "strncmp")] +pub unsafe fn strncmp(s1: *const CChar, s2: *const CChar, n: usize) -> crate::CInt { for i in 0..n as isize { let s1_i = s1.offset(i); let s2_i = s2.offset(i); @@ -34,7 +29,7 @@ mod test { fn matches() { let a = b"123\0"; let b = b"1234\0"; - let result = unsafe { r_strncmp(a.as_ptr(), b.as_ptr(), 3) }; + let result = unsafe { strncmp(a.as_ptr(), b.as_ptr(), 3) }; // Match! assert_eq!(result, 0); } @@ -43,7 +38,7 @@ mod test { fn no_match() { let a = b"123\0"; let b = b"x1234\0"; - let result = unsafe { r_strncmp(a.as_ptr(), b.as_ptr(), 3) }; + let result = unsafe { strncmp(a.as_ptr(), b.as_ptr(), 3) }; // No match, first string first assert!(result < 0); } @@ -52,7 +47,7 @@ mod test { fn no_match2() { let a = b"bbbbb\0"; let b = b"aaaaa\0"; - let result = unsafe { r_strncmp(a.as_ptr(), b.as_ptr(), 3) }; + let result = unsafe { strncmp(a.as_ptr(), b.as_ptr(), 3) }; // No match, second string first assert!(result > 0); } diff --git a/src/strncpy.rs b/src/strncpy.rs index a154e80..aaedafa 100644 --- a/src/strncpy.rs +++ b/src/strncpy.rs @@ -7,17 +7,8 @@ use crate::CChar; /// Rust implementation of C library function `strncmp`. Passing NULL /// (core::ptr::null()) gives undefined behaviour. -#[cfg(feature = "strncpy")] -#[no_mangle] -pub unsafe extern "C" fn strncpy( - dest: *mut CChar, - src: *const CChar, - count: usize, -) -> *const CChar { - r_strncpy(dest, src, count) -} - -pub(crate) unsafe fn r_strncpy(dest: *mut CChar, src: *const CChar, count: usize) -> *const CChar { +#[cfg_attr(feature = "strncpy", export_name = "strncpy")] +pub unsafe fn strncpy(dest: *mut CChar, src: *const CChar, count: usize) -> *const CChar { let mut i = 0isize; while i < count as isize { *dest.offset(i) = *src.offset(i); @@ -41,7 +32,7 @@ mod test { fn short() { let src = b"hi\0"; let mut dest = *b"abcdef"; // no null terminator - let result = unsafe { r_strncpy(dest.as_mut_ptr(), src.as_ptr(), 5) }; + let result = unsafe { strncpy(dest.as_mut_ptr(), src.as_ptr(), 5) }; assert_eq!( unsafe { core::slice::from_raw_parts(result, 5) }, *b"hi\0\0\0" @@ -52,7 +43,7 @@ mod test { fn two() { let src = b"hi\0"; let mut dest = [0u8; 2]; // no space for null terminator - let result = unsafe { r_strncpy(dest.as_mut_ptr(), src.as_ptr(), 2) }; + let result = unsafe { strncpy(dest.as_mut_ptr(), src.as_ptr(), 2) }; assert_eq!(unsafe { core::slice::from_raw_parts(result, 2) }, b"hi"); } } diff --git a/src/strstr.rs b/src/strstr.rs index 03fb130..d848d2d 100644 --- a/src/strstr.rs +++ b/src/strstr.rs @@ -5,14 +5,10 @@ use crate::{CChar, CStringIter}; -#[cfg(feature = "strstr")] -#[no_mangle] -pub unsafe extern "C" fn strstr(haystack: *const CChar, needle: *const CChar) -> *const CChar { - r_strstr(haystack, needle) -} /// Rust implementation of C library function `strstr` -pub(crate) unsafe extern "C" fn r_strstr( +#[cfg_attr(feature = "strstr", export_name = "strstr")] +pub unsafe extern "C" fn strstr( haystack: *const CChar, needle: *const CChar, ) -> *const CChar { @@ -46,7 +42,7 @@ mod test { fn no_match() { let needle = b"needle\0".as_ptr(); let haystack = b"haystack\0".as_ptr(); - let result = unsafe { r_strstr(haystack, needle) }; + let result = unsafe { strstr(haystack, needle) }; assert_eq!(result, core::ptr::null()); } @@ -54,7 +50,7 @@ mod test { fn start() { let needle = b"hay\0".as_ptr(); let haystack = b"haystack\0".as_ptr(); - let result = unsafe { r_strstr(haystack, needle) }; + let result = unsafe { strstr(haystack, needle) }; assert_eq!(result, haystack); } @@ -62,7 +58,7 @@ mod test { fn middle() { let needle = b"yst\0".as_ptr(); let haystack = b"haystack\0".as_ptr(); - let result = unsafe { r_strstr(haystack, needle) }; + let result = unsafe { strstr(haystack, needle) }; assert_eq!(result, unsafe { haystack.offset(2) }); } @@ -70,7 +66,7 @@ mod test { fn end() { let needle = b"stack\0".as_ptr(); let haystack = b"haystack\0".as_ptr(); - let result = unsafe { r_strstr(haystack, needle) }; + let result = unsafe { strstr(haystack, needle) }; assert_eq!(result, unsafe { haystack.offset(3) }); } @@ -78,7 +74,7 @@ mod test { fn partial() { let needle = b"haystacka\0".as_ptr(); let haystack = b"haystack\0".as_ptr(); - let result = unsafe { r_strstr(haystack, needle) }; + let result = unsafe { strstr(haystack, needle) }; assert_eq!(result, core::ptr::null()); } } diff --git a/src/strtol.rs b/src/strtol.rs index 377c258..e47d07e 100644 --- a/src/strtol.rs +++ b/src/strtol.rs @@ -3,99 +3,55 @@ //! Original code from the `c-ward` project. //! Licensed under the MIT license. -use crate::{errno::*, CChar, CInt, CIntMax, CLong, CLongLong, CUIntMax, CULong, CULongLong}; +use crate::{CChar, CInt, CIntMax, CLong, CLongLong, CUIntMax, CULong, CULongLong}; -#[cfg(feature = "atoi")] -#[no_mangle] +/// Rust implementation of C library function `atoi` +#[cfg_attr(feature = "atoi", no_mangle)] pub unsafe extern "C" fn atoi(s: *const CChar) -> CInt { - r_atoi(s) + strtol(s, core::ptr::null_mut(), 10) as CInt } -#[cfg(feature = "strtol")] -#[no_mangle] -pub unsafe extern "C" fn strtol(s: *const CChar, endptr: *mut *const CChar, base: CInt) -> CLong { - r_strtol(s, endptr, base) +/// Rust implementation of C library function `atol` +#[cfg_attr(feature = "strtol", no_mangle)] +pub unsafe fn strtol(s: *const CChar, endptr: *mut *const CChar, base: CInt) -> CLong { + strtox(s, endptr, base, CLong::MIN as _, CLong::MAX as _) as CLong } -#[cfg(feature = "strtoul")] -//#[no_mangle] -pub unsafe extern "C" fn strtoul(s: *const CChar, endptr: *mut *const CChar, base: CInt) -> CULong { - r_strtoul(s, endptr, base) +/// Rust implementation of C library function `strtoul` +#[cfg_attr(feature = "strtoul", no_mangle)] +pub unsafe fn strtoul(s: *const CChar, endptr: *mut *const CChar, base: CInt) -> CULong { + strtox(s, endptr, base, 0, CULong::MAX as _) as CULong } -#[cfg(feature = "strtoll")] -#[no_mangle] -pub unsafe extern "C" fn strtoll( - s: *const CChar, - endptr: *mut *const CChar, - base: CInt, -) -> CLongLong { - r_strtoll(s, endptr, base) -} - -#[cfg(feature = "strtoull")] -#[no_mangle] -pub unsafe extern "C" fn strtoull( - s: *const CChar, - endptr: *mut *const CChar, - base: CInt, -) -> CULongLong { - r_strtoull(s, endptr, base) -} - -#[cfg(feature = "strtoimax")] -#[no_mangle] -pub unsafe extern "C" fn strtoimax( - s: *const CChar, - endptr: *mut *const CChar, - base: CInt, -) -> CIntMax { - r_strtoimax(s, endptr, base) -} - -#[cfg(feature = "strtoumax")] -#[no_mangle] -pub unsafe extern "C" fn strtoumax( - s: *const CChar, - endptr: *mut *const CChar, - base: CInt, -) -> CUIntMax { - r_strtoumax(s, endptr, base) -} - -pub(crate) unsafe fn r_atoi(s: *const CChar) -> CInt { - r_strtol(s, core::ptr::null_mut(), 10) as CInt -} - -pub(crate) unsafe fn r_strtol(s: *const CChar, endptr: *mut *const CChar, base: CInt) -> CLong { - r_strtox(s, endptr, base, CLong::MIN as _, CLong::MAX as _) as CLong -} - -pub(crate) unsafe fn r_strtoul(s: *const CChar, endptr: *mut *const CChar, base: CInt) -> CULong { - r_strtox(s, endptr, base, 0, CULong::MAX as _) as CULong -} - -pub(crate) unsafe fn r_strtoll(s: *const CChar, endptr: *mut *const CChar, base: CInt) -> CLongLong { - r_strtox(s, endptr, base, CLongLong::MIN, CLongLong::MAX as _) as CLongLong +/// Rust implementation of C library function `strtoll` +#[cfg_attr(feature = "strtoll", no_mangle)] +pub unsafe fn strtoll(s: *const CChar, endptr: *mut *const CChar, base: CInt) -> CLongLong { + strtox(s, endptr, base, CLongLong::MIN, CLongLong::MAX as _) as CLongLong } -pub(crate) unsafe fn r_strtoull( +/// Rust implementation of C library function `strtoull` +#[cfg_attr(feature = "strtoull", no_mangle)] +pub unsafe fn strtoull( s: *const CChar, endptr: *mut *const CChar, base: CInt, ) -> CULongLong { - r_strtox(s, endptr, base, 0, CULongLong::MAX) as CULongLong + strtox(s, endptr, base, 0, CULongLong::MAX) as CULongLong } -pub(crate) unsafe fn r_strtoimax(s: *const CChar, endptr: *mut *const CChar, base: CInt) -> CIntMax { - r_strtox(s, endptr, base, CIntMax::MIN, CIntMax::MAX as _) as CIntMax +/// Rust implementation of C library function `strtoimax` +#[cfg_attr(feature = "strtoimax", no_mangle)] +pub unsafe fn strtoimax(s: *const CChar, endptr: *mut *const CChar, base: CInt) -> CIntMax { + strtox(s, endptr, base, CIntMax::MIN, CIntMax::MAX as _) as CIntMax } -pub(crate) unsafe fn r_strtoumax(s: *const CChar, endptr: *mut *const CChar, base: CInt) -> CUIntMax { - r_strtox(s, endptr, base, 0, CUIntMax::MAX) as CUIntMax +/// Rust implementation of C library function `strtoumax` +#[cfg_attr(feature = "strtoumax", no_mangle)] +pub unsafe fn strtoumax(s: *const CChar, endptr: *mut *const CChar, base: CInt) -> CUIntMax { + strtox(s, endptr, base, 0, CUIntMax::MAX) as CUIntMax } -pub(crate) unsafe fn r_strtox( +pub unsafe fn strtox( s: *const CChar, endptr: *mut *const CChar, base: CInt, @@ -103,13 +59,13 @@ pub(crate) unsafe fn r_strtox( max: CUIntMax, ) -> CUIntMax { if !(0..=36).contains(&base) { - set_errno(errno(EINVAL)); + // TODO: set errno to EINVAL return 0; } // Skip leading whitespace. let mut s = s; - while r_isspace(CInt::from(*s)) != 0 { + while isspace(CInt::from(*s)) != 0 { s = s.add(1); } @@ -190,7 +146,7 @@ pub(crate) unsafe fn r_strtox( // Report overflow. if overflow { - set_errno(errno(ERANGE)); + // TODO: set errno to ERANGE return if negate && min != 0 { min as CUIntMax } else { @@ -207,46 +163,30 @@ pub(crate) unsafe fn r_strtox( num as CUIntMax } -#[cfg(feature = "isspace")] -#[no_mangle] -pub unsafe extern "C" fn isspace(c: CInt) -> CInt { - r_isspace(c) as CInt -} - -#[cfg(feature = "isdigit")] -#[no_mangle] -pub unsafe extern "C" fn isdigit(c: CInt) -> CInt { - r_isdigit(c) as CInt -} - -#[cfg(feature = "isalpha")] -#[no_mangle] -pub unsafe extern "C" fn isalpha(c: CInt) -> CInt { - r_isalpha(c) as CInt -} - -#[cfg(feature = "isupper")] -#[no_mangle] -pub unsafe extern "C" fn isupper(c: CInt) -> CInt { - r_isupper(c) as CInt -} - -pub(crate) fn r_isspace(argument: CInt) -> CInt { +/// Rust implementation of C library function `isspace` +#[cfg_attr(feature = "isspace", export_name = "isspace")] +pub fn isspace(argument: CInt) -> CInt { match argument as CChar { b' ' | b'\t' | b'\n' | b'\r' | 0x0b | 0x0c => 1, _ => 0, } } -pub(crate) fn r_isdigit(argument: CInt) -> CInt { +/// Rust implementation of C library function `isdigit` +#[cfg_attr(feature = "isdigit", export_name = "isdigit")] +pub fn isdigit(argument: CInt) -> CInt { (argument as CChar).is_ascii_digit() as CInt } -pub(crate) fn r_isalpha(argument: CInt) -> CInt { +/// Rust implementation of C library function `isalpha` +#[cfg_attr(feature = "isalpha", export_name = "isalpha")] +pub fn isalpha(argument: CInt) -> CInt { (argument as CChar).is_ascii_alphabetic() as CInt } -pub(crate) fn r_isupper(argument: CInt) -> CInt { +/// Rust implementation of C library function `isupper` +#[cfg_attr(feature = "isupper", export_name = "isupper")] +pub fn isupper(argument: CInt) -> CInt { (argument as CChar).is_ascii_uppercase() as CInt } @@ -269,7 +209,7 @@ mod tests { ]; for (result_number, result_ptr) in results { - let number = unsafe { r_strtoul(s, &mut s as *mut _, 10) }; + let number = unsafe { strtoul(s, &mut s as *mut _, 10) }; assert_eq!(s, result_ptr); assert_eq!(number, result_number); @@ -279,15 +219,15 @@ mod tests { #[test] fn parse_hex() { assert_eq!( - unsafe { r_strtoul(b"0xAA123\0".as_ptr(), null_mut(), 0) }, + unsafe { strtoul(b"0xAA123\0".as_ptr(), null_mut(), 0) }, 0xAA123 ); assert_eq!( - unsafe { r_strtoul(b"0X00\0".as_ptr(), null_mut(), 0) }, + unsafe { strtoul(b"0X00\0".as_ptr(), null_mut(), 0) }, 0x00 ); assert_eq!( - unsafe { r_strtoul(b"-0x123456F\0".as_ptr(), null_mut(), 0) }, + unsafe { strtoul(b"-0x123456F\0".as_ptr(), null_mut(), 0) }, (-0x123456Fi32) as _ ); } From 4f7873725dd3b8dec55dc62771b0652fca66ba46 Mon Sep 17 00:00:00 2001 From: Myung Gyungmin Date: Sat, 20 Jan 2024 16:58:01 +0900 Subject: [PATCH 14/20] unify all as no_mangle --- src/abs.rs | 2 +- src/strchr.rs | 2 +- src/strcmp.rs | 2 +- src/strcpy.rs | 2 +- src/strlen.rs | 2 +- src/strncmp.rs | 2 +- src/strncpy.rs | 2 +- src/strstr.rs | 2 +- src/strtol.rs | 8 ++++---- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/abs.rs b/src/abs.rs index e89befc..22a1871 100644 --- a/src/abs.rs +++ b/src/abs.rs @@ -10,7 +10,7 @@ use crate::CInt; /// use tinyrlibc::abs; /// assert_eq!(abs(-2), 2); /// ``` -#[cfg_attr(feature = "abs", export_name = "abs")] +#[cfg_attr(feature = "abs", no_mangle)] pub fn abs(i: CInt) -> CInt { i.abs() } diff --git a/src/strchr.rs b/src/strchr.rs index de9113c..bf27f8e 100644 --- a/src/strchr.rs +++ b/src/strchr.rs @@ -6,7 +6,7 @@ use crate::{CChar, CInt}; /// Rust implementation of C library function `strchr` -#[cfg_attr(feature = "strchr", export_name = "strchr")] +#[cfg_attr(feature = "strchr", no_mangle)] pub unsafe fn strchr(haystack: *const CChar, needle: CInt) -> *const CChar { for idx in 0.. { let ptr = haystack.offset(idx); diff --git a/src/strcmp.rs b/src/strcmp.rs index c147506..49172b9 100644 --- a/src/strcmp.rs +++ b/src/strcmp.rs @@ -6,7 +6,7 @@ use crate::{CChar, CInt}; /// Rust implementation of C library function `strcmp` -#[cfg_attr(feature = "strcmp", export_name = "strcmp")] +#[cfg_attr(feature = "strcmp", no_mangle)] pub unsafe fn strcmp(s1: *const CChar, s2: *const CChar) -> CInt { for i in 0.. { let s1_i = s1.offset(i); diff --git a/src/strcpy.rs b/src/strcpy.rs index 2fc03c9..a3ce369 100644 --- a/src/strcpy.rs +++ b/src/strcpy.rs @@ -6,7 +6,7 @@ use crate::CChar; /// Rust implementation of C library function `strcpy`. Passing NULL /// (core::ptr::null()) gives undefined behaviour. -#[cfg_attr(feature = "strcpy", export_name = "strcpy")] +#[cfg_attr(feature = "strcpy", no_mangle)] pub unsafe fn strcpy(dest: *mut CChar, src: *const CChar) -> *const CChar { let mut i = 0; loop { diff --git a/src/strlen.rs b/src/strlen.rs index 2a1b7c1..a361aef 100644 --- a/src/strlen.rs +++ b/src/strlen.rs @@ -6,7 +6,7 @@ use crate::CChar; /// Rust implementation of C library function `strlen` -#[cfg_attr(feature = "strlen", export_name = "strlen")] +#[cfg_attr(feature = "strlen", no_mangle)] pub unsafe fn strlen(mut s: *const CChar) -> usize { let mut result = 0; while *s != 0 { diff --git a/src/strncmp.rs b/src/strncmp.rs index dbd45c4..e93c710 100644 --- a/src/strncmp.rs +++ b/src/strncmp.rs @@ -7,7 +7,7 @@ use crate::{CChar, CInt}; /// Rust implementation of C library function `strncmp`. Passing NULL /// (core::ptr::null()) gives undefined behaviour. -#[cfg_attr(feature = "strncmp", export_name = "strncmp")] +#[cfg_attr(feature = "strncmp", no_mangle)] pub unsafe fn strncmp(s1: *const CChar, s2: *const CChar, n: usize) -> crate::CInt { for i in 0..n as isize { let s1_i = s1.offset(i); diff --git a/src/strncpy.rs b/src/strncpy.rs index aaedafa..4c61605 100644 --- a/src/strncpy.rs +++ b/src/strncpy.rs @@ -7,7 +7,7 @@ use crate::CChar; /// Rust implementation of C library function `strncmp`. Passing NULL /// (core::ptr::null()) gives undefined behaviour. -#[cfg_attr(feature = "strncpy", export_name = "strncpy")] +#[cfg_attr(feature = "strncpy", no_mangle)] pub unsafe fn strncpy(dest: *mut CChar, src: *const CChar, count: usize) -> *const CChar { let mut i = 0isize; while i < count as isize { diff --git a/src/strstr.rs b/src/strstr.rs index d848d2d..4149541 100644 --- a/src/strstr.rs +++ b/src/strstr.rs @@ -7,7 +7,7 @@ use crate::{CChar, CStringIter}; /// Rust implementation of C library function `strstr` -#[cfg_attr(feature = "strstr", export_name = "strstr")] +#[cfg_attr(feature = "strstr", no_mangle)] pub unsafe extern "C" fn strstr( haystack: *const CChar, needle: *const CChar, diff --git a/src/strtol.rs b/src/strtol.rs index e47d07e..a1b3c78 100644 --- a/src/strtol.rs +++ b/src/strtol.rs @@ -164,7 +164,7 @@ pub unsafe fn strtox( } /// Rust implementation of C library function `isspace` -#[cfg_attr(feature = "isspace", export_name = "isspace")] +#[cfg_attr(feature = "isspace", no_mangle)] pub fn isspace(argument: CInt) -> CInt { match argument as CChar { b' ' | b'\t' | b'\n' | b'\r' | 0x0b | 0x0c => 1, @@ -173,19 +173,19 @@ pub fn isspace(argument: CInt) -> CInt { } /// Rust implementation of C library function `isdigit` -#[cfg_attr(feature = "isdigit", export_name = "isdigit")] +#[cfg_attr(feature = "isdigit", no_mangle)] pub fn isdigit(argument: CInt) -> CInt { (argument as CChar).is_ascii_digit() as CInt } /// Rust implementation of C library function `isalpha` -#[cfg_attr(feature = "isalpha", export_name = "isalpha")] +#[cfg_attr(feature = "isalpha", no_mangle)] pub fn isalpha(argument: CInt) -> CInt { (argument as CChar).is_ascii_alphabetic() as CInt } /// Rust implementation of C library function `isupper` -#[cfg_attr(feature = "isupper", export_name = "isupper")] +#[cfg_attr(feature = "isupper", no_mangle)] pub fn isupper(argument: CInt) -> CInt { (argument as CChar).is_ascii_uppercase() as CInt } From 2ce9ba50eeb6d653a9fa8a25540a73c89a44ea91 Mon Sep 17 00:00:00 2001 From: Myung Gyungmin Date: Sun, 21 Jan 2024 13:47:48 +0900 Subject: [PATCH 15/20] add extern c, add macro to snprintf.c --- Cargo.toml | 2 ++ build.rs | 34 +++++++++++++++++++++++++++++++--- src/abs.rs | 7 +------ src/itoa.rs | 6 ++++-- src/strchr.rs | 2 +- src/strcmp.rs | 2 +- src/strcpy.rs | 2 +- src/strlen.rs | 2 +- src/strncmp.rs | 2 +- src/strncpy.rs | 2 +- src/strtol.rs | 21 +++++++++++---------- 11 files changed, 55 insertions(+), 27 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a6f85f0..d381c10 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ all = [ "strstr", "strchr", "atoi", + "utoa", "itoa", "snprintf", "errno", @@ -55,6 +56,7 @@ strtoumax = [] strstr = [] strchr = [] atoi = [] +utoa = [] itoa = [] snprintf = [] errno = [] diff --git a/build.rs b/build.rs index d3dee7b..1e31123 100644 --- a/build.rs +++ b/build.rs @@ -1,12 +1,40 @@ fn main() { if cfg!(feature = "snprintf") { // Build our snprintf substitute (which has to be C as Rust doesn't do varargs) - cc::Build::new() - .warnings(true) + let mut build = cc::Build::new() + .warnings(true) .extra_warnings(true) .flag("-std=c99") .file("./src/snprintf.c") - .compile("clocal"); + .clone(); + + #[cfg(feature = "itoa")] + { + build = build.define("itoa", "itoa").clone(); + } + #[cfg(feature = "utoa")] + { + build = build.define("utoa", "utoa").clone(); + } + #[cfg(feature = "strtoul")] + { + build = build.define("strtoul", "strtoul").clone(); + } + #[cfg(not(feature = "itoa"))] + { + build = build.define("itoa", "tinyrlibc_itoa").clone(); + } + #[cfg(not(feature = "utoa"))] + { + build = build.define("utoa", "tinyrlibc_utoa").clone(); + } + #[cfg(not(feature = "strtoul"))] + { + build = build.define("strtoul", "tinyrlibc_strtoul").clone(); + } + + + build.compile("clocal"); } println!("cargo:rerun-if-changed=build.rs"); diff --git a/src/abs.rs b/src/abs.rs index 22a1871..f42f4ec 100644 --- a/src/abs.rs +++ b/src/abs.rs @@ -5,13 +5,8 @@ use crate::CInt; /// Rust implementation of C library function `abs` -/// -/// ``` -/// use tinyrlibc::abs; -/// assert_eq!(abs(-2), 2); -/// ``` #[cfg_attr(feature = "abs", no_mangle)] -pub fn abs(i: CInt) -> CInt { +pub extern "C" fn abs(i: CInt) -> CInt { i.abs() } diff --git a/src/itoa.rs b/src/itoa.rs index 892dbce..9d484ed 100644 --- a/src/itoa.rs +++ b/src/itoa.rs @@ -15,6 +15,7 @@ use crate::CChar; /// /// Returns the number of bytes written on success (not including the null), /// or -1 if the buffer wasn't large enough. +#[cfg_attr(not(feature = "itoa"), export_name = "tinyrlibc_itoa")] #[cfg_attr(feature = "itoa", no_mangle)] pub unsafe extern "C" fn itoa(i: i64, s: *mut CChar, s_len: usize, radix: u8) -> i32 { let (is_negative, pos_i) = if i < 0 { @@ -38,8 +39,9 @@ pub unsafe extern "C" fn itoa(i: i64, s: *mut CChar, s_len: usize, radix: u8) -> /// /// Returns the number of bytes written on success (not including the null), /// or -1 if the buffer wasn't large enough. -#[cfg_attr(feature = "itoa", no_mangle)] -unsafe extern "C" fn utoa(mut u: u64, s: *mut CChar, s_len: usize, radix: u8) -> i32 { +#[cfg_attr(not(feature = "utoa"), export_name = "tinyrlibc_utoa")] +#[cfg_attr(feature = "utoa", no_mangle)] +pub unsafe extern "C" fn utoa(mut u: u64, s: *mut CChar, s_len: usize, radix: u8) -> i32 { let buffer_slice = core::slice::from_raw_parts_mut(s, s_len); // Build the number up in buffer s in reverse order diff --git a/src/strchr.rs b/src/strchr.rs index bf27f8e..063c96a 100644 --- a/src/strchr.rs +++ b/src/strchr.rs @@ -7,7 +7,7 @@ use crate::{CChar, CInt}; /// Rust implementation of C library function `strchr` #[cfg_attr(feature = "strchr", no_mangle)] -pub unsafe fn strchr(haystack: *const CChar, needle: CInt) -> *const CChar { +pub unsafe extern "C" fn strchr(haystack: *const CChar, needle: CInt) -> *const CChar { for idx in 0.. { let ptr = haystack.offset(idx); if needle == (*ptr) as CInt { diff --git a/src/strcmp.rs b/src/strcmp.rs index 49172b9..c170463 100644 --- a/src/strcmp.rs +++ b/src/strcmp.rs @@ -7,7 +7,7 @@ use crate::{CChar, CInt}; /// Rust implementation of C library function `strcmp` #[cfg_attr(feature = "strcmp", no_mangle)] -pub unsafe fn strcmp(s1: *const CChar, s2: *const CChar) -> CInt { +pub unsafe extern "C" fn strcmp(s1: *const CChar, s2: *const CChar) -> CInt { for i in 0.. { let s1_i = s1.offset(i); let s2_i = s2.offset(i); diff --git a/src/strcpy.rs b/src/strcpy.rs index a3ce369..d6deced 100644 --- a/src/strcpy.rs +++ b/src/strcpy.rs @@ -7,7 +7,7 @@ use crate::CChar; /// Rust implementation of C library function `strcpy`. Passing NULL /// (core::ptr::null()) gives undefined behaviour. #[cfg_attr(feature = "strcpy", no_mangle)] -pub unsafe fn strcpy(dest: *mut CChar, src: *const CChar) -> *const CChar { +pub unsafe extern "C" fn strcpy(dest: *mut CChar, src: *const CChar) -> *const CChar { let mut i = 0; loop { *dest.offset(i) = *src.offset(i); diff --git a/src/strlen.rs b/src/strlen.rs index a361aef..a81ed3d 100644 --- a/src/strlen.rs +++ b/src/strlen.rs @@ -7,7 +7,7 @@ use crate::CChar; /// Rust implementation of C library function `strlen` #[cfg_attr(feature = "strlen", no_mangle)] -pub unsafe fn strlen(mut s: *const CChar) -> usize { +pub unsafe extern "C" fn strlen(mut s: *const CChar) -> usize { let mut result = 0; while *s != 0 { s = s.offset(1); diff --git a/src/strncmp.rs b/src/strncmp.rs index e93c710..d081a52 100644 --- a/src/strncmp.rs +++ b/src/strncmp.rs @@ -8,7 +8,7 @@ use crate::{CChar, CInt}; /// Rust implementation of C library function `strncmp`. Passing NULL /// (core::ptr::null()) gives undefined behaviour. #[cfg_attr(feature = "strncmp", no_mangle)] -pub unsafe fn strncmp(s1: *const CChar, s2: *const CChar, n: usize) -> crate::CInt { +pub unsafe extern "C" fn strncmp(s1: *const CChar, s2: *const CChar, n: usize) -> crate::CInt { for i in 0..n as isize { let s1_i = s1.offset(i); let s2_i = s2.offset(i); diff --git a/src/strncpy.rs b/src/strncpy.rs index 4c61605..7dd0ec3 100644 --- a/src/strncpy.rs +++ b/src/strncpy.rs @@ -8,7 +8,7 @@ use crate::CChar; /// Rust implementation of C library function `strncmp`. Passing NULL /// (core::ptr::null()) gives undefined behaviour. #[cfg_attr(feature = "strncpy", no_mangle)] -pub unsafe fn strncpy(dest: *mut CChar, src: *const CChar, count: usize) -> *const CChar { +pub unsafe extern "C" fn strncpy(dest: *mut CChar, src: *const CChar, count: usize) -> *const CChar { let mut i = 0isize; while i < count as isize { *dest.offset(i) = *src.offset(i); diff --git a/src/strtol.rs b/src/strtol.rs index a1b3c78..bd01e90 100644 --- a/src/strtol.rs +++ b/src/strtol.rs @@ -13,25 +13,26 @@ pub unsafe extern "C" fn atoi(s: *const CChar) -> CInt { /// Rust implementation of C library function `atol` #[cfg_attr(feature = "strtol", no_mangle)] -pub unsafe fn strtol(s: *const CChar, endptr: *mut *const CChar, base: CInt) -> CLong { +pub unsafe extern "C" fn strtol(s: *const CChar, endptr: *mut *const CChar, base: CInt) -> CLong { strtox(s, endptr, base, CLong::MIN as _, CLong::MAX as _) as CLong } /// Rust implementation of C library function `strtoul` +#[cfg_attr(not(feature = "strtoul"), export_name = "tinyrlibc_strtoul")] #[cfg_attr(feature = "strtoul", no_mangle)] -pub unsafe fn strtoul(s: *const CChar, endptr: *mut *const CChar, base: CInt) -> CULong { +pub unsafe extern "C" fn strtoul(s: *const CChar, endptr: *mut *const CChar, base: CInt) -> CULong { strtox(s, endptr, base, 0, CULong::MAX as _) as CULong } /// Rust implementation of C library function `strtoll` #[cfg_attr(feature = "strtoll", no_mangle)] -pub unsafe fn strtoll(s: *const CChar, endptr: *mut *const CChar, base: CInt) -> CLongLong { +pub unsafe extern "C" fn strtoll(s: *const CChar, endptr: *mut *const CChar, base: CInt) -> CLongLong { strtox(s, endptr, base, CLongLong::MIN, CLongLong::MAX as _) as CLongLong } /// Rust implementation of C library function `strtoull` #[cfg_attr(feature = "strtoull", no_mangle)] -pub unsafe fn strtoull( +pub unsafe extern "C" fn strtoull( s: *const CChar, endptr: *mut *const CChar, base: CInt, @@ -41,13 +42,13 @@ pub unsafe fn strtoull( /// Rust implementation of C library function `strtoimax` #[cfg_attr(feature = "strtoimax", no_mangle)] -pub unsafe fn strtoimax(s: *const CChar, endptr: *mut *const CChar, base: CInt) -> CIntMax { +pub unsafe extern "C" fn strtoimax(s: *const CChar, endptr: *mut *const CChar, base: CInt) -> CIntMax { strtox(s, endptr, base, CIntMax::MIN, CIntMax::MAX as _) as CIntMax } /// Rust implementation of C library function `strtoumax` #[cfg_attr(feature = "strtoumax", no_mangle)] -pub unsafe fn strtoumax(s: *const CChar, endptr: *mut *const CChar, base: CInt) -> CUIntMax { +pub unsafe extern "C" fn strtoumax(s: *const CChar, endptr: *mut *const CChar, base: CInt) -> CUIntMax { strtox(s, endptr, base, 0, CUIntMax::MAX) as CUIntMax } @@ -165,7 +166,7 @@ pub unsafe fn strtox( /// Rust implementation of C library function `isspace` #[cfg_attr(feature = "isspace", no_mangle)] -pub fn isspace(argument: CInt) -> CInt { +pub extern "C" fn isspace(argument: CInt) -> CInt { match argument as CChar { b' ' | b'\t' | b'\n' | b'\r' | 0x0b | 0x0c => 1, _ => 0, @@ -174,19 +175,19 @@ pub fn isspace(argument: CInt) -> CInt { /// Rust implementation of C library function `isdigit` #[cfg_attr(feature = "isdigit", no_mangle)] -pub fn isdigit(argument: CInt) -> CInt { +pub extern "C" fn isdigit(argument: CInt) -> CInt { (argument as CChar).is_ascii_digit() as CInt } /// Rust implementation of C library function `isalpha` #[cfg_attr(feature = "isalpha", no_mangle)] -pub fn isalpha(argument: CInt) -> CInt { +pub extern "C" fn isalpha(argument: CInt) -> CInt { (argument as CChar).is_ascii_alphabetic() as CInt } /// Rust implementation of C library function `isupper` #[cfg_attr(feature = "isupper", no_mangle)] -pub fn isupper(argument: CInt) -> CInt { +pub extern "C" fn isupper(argument: CInt) -> CInt { (argument as CChar).is_ascii_uppercase() as CInt } From a5eb74bfc52b42d601e6ffbc23c694cfec0f0ec1 Mon Sep 17 00:00:00 2001 From: Myung Gyungmin Date: Sun, 21 Jan 2024 22:01:23 +0900 Subject: [PATCH 16/20] fmt --- build.rs | 57 +++++++++++++++++++++++++------------------------- src/lib.rs | 24 ++++++++++----------- src/strncpy.rs | 6 +++++- src/strstr.rs | 6 +----- src/strtol.rs | 27 ++++++++++++++++-------- 5 files changed, 64 insertions(+), 56 deletions(-) diff --git a/build.rs b/build.rs index 1e31123..8d572c3 100644 --- a/build.rs +++ b/build.rs @@ -2,41 +2,40 @@ fn main() { if cfg!(feature = "snprintf") { // Build our snprintf substitute (which has to be C as Rust doesn't do varargs) let mut build = cc::Build::new() - .warnings(true) + .warnings(true) .extra_warnings(true) .flag("-std=c99") .file("./src/snprintf.c") - .clone(); - - #[cfg(feature = "itoa")] - { - build = build.define("itoa", "itoa").clone(); - } - #[cfg(feature = "utoa")] - { - build = build.define("utoa", "utoa").clone(); - } - #[cfg(feature = "strtoul")] - { - build = build.define("strtoul", "strtoul").clone(); - } - #[cfg(not(feature = "itoa"))] - { - build = build.define("itoa", "tinyrlibc_itoa").clone(); - } - #[cfg(not(feature = "utoa"))] - { - build = build.define("utoa", "tinyrlibc_utoa").clone(); - } - #[cfg(not(feature = "strtoul"))] - { - build = build.define("strtoul", "tinyrlibc_strtoul").clone(); - } + .clone(); + #[cfg(feature = "itoa")] + { + build = build.define("itoa", "itoa").clone(); + } + #[cfg(feature = "utoa")] + { + build = build.define("utoa", "utoa").clone(); + } + #[cfg(feature = "strtoul")] + { + build = build.define("strtoul", "strtoul").clone(); + } + #[cfg(not(feature = "itoa"))] + { + build = build.define("itoa", "tinyrlibc_itoa").clone(); + } + #[cfg(not(feature = "utoa"))] + { + build = build.define("utoa", "tinyrlibc_utoa").clone(); + } + #[cfg(not(feature = "strtoul"))] + { + build = build.define("strtoul", "tinyrlibc_strtoul").clone(); + } build.compile("clocal"); } - println!("cargo:rerun-if-changed=build.rs"); - println!("cargo:rerun-if-changed=src/snprintf.c"); + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=src/snprintf.c"); } diff --git a/src/lib.rs b/src/lib.rs index 5e37ca7..6214010 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -43,26 +43,26 @@ pub use self::strlen::strlen; mod strtol; #[cfg(feature = "atoi")] pub use self::strtol::atoi; +#[cfg(feature = "isalpha")] +pub use self::strtol::isalpha; +#[cfg(feature = "isdigit")] +pub use self::strtol::isdigit; +#[cfg(feature = "isspace")] +pub use self::strtol::isspace; +#[cfg(feature = "isupper")] +pub use self::strtol::isupper; +#[cfg(feature = "strtoimax")] +pub use self::strtol::strtoimax; #[cfg(feature = "strtol")] pub use self::strtol::strtol; -#[cfg(feature = "strtoul")] -pub use self::strtol::strtoul; #[cfg(feature = "strtoll")] pub use self::strtol::strtoll; +#[cfg(feature = "strtoul")] +pub use self::strtol::strtoul; #[cfg(feature = "strtoull")] pub use self::strtol::strtoull; -#[cfg(feature = "strtoimax")] -pub use self::strtol::strtoimax; #[cfg(feature = "strtoumax")] pub use self::strtol::strtoumax; -#[cfg(feature = "isspace")] -pub use self::strtol::isspace; -#[cfg(feature = "isalpha")] -pub use self::strtol::isalpha; -#[cfg(feature = "isdigit")] -pub use self::strtol::isdigit; -#[cfg(feature = "isupper")] -pub use self::strtol::isupper; mod strstr; #[cfg(feature = "strstr")] diff --git a/src/strncpy.rs b/src/strncpy.rs index 7dd0ec3..d911c26 100644 --- a/src/strncpy.rs +++ b/src/strncpy.rs @@ -8,7 +8,11 @@ use crate::CChar; /// Rust implementation of C library function `strncmp`. Passing NULL /// (core::ptr::null()) gives undefined behaviour. #[cfg_attr(feature = "strncpy", no_mangle)] -pub unsafe extern "C" fn strncpy(dest: *mut CChar, src: *const CChar, count: usize) -> *const CChar { +pub unsafe extern "C" fn strncpy( + dest: *mut CChar, + src: *const CChar, + count: usize, +) -> *const CChar { let mut i = 0isize; while i < count as isize { *dest.offset(i) = *src.offset(i); diff --git a/src/strstr.rs b/src/strstr.rs index 4149541..692962f 100644 --- a/src/strstr.rs +++ b/src/strstr.rs @@ -5,13 +5,9 @@ use crate::{CChar, CStringIter}; - /// Rust implementation of C library function `strstr` #[cfg_attr(feature = "strstr", no_mangle)] -pub unsafe extern "C" fn strstr( - haystack: *const CChar, - needle: *const CChar, -) -> *const CChar { +pub unsafe extern "C" fn strstr(haystack: *const CChar, needle: *const CChar) -> *const CChar { if *needle.offset(0) == 0 { return haystack; } diff --git a/src/strtol.rs b/src/strtol.rs index bd01e90..1de8973 100644 --- a/src/strtol.rs +++ b/src/strtol.rs @@ -26,7 +26,11 @@ pub unsafe extern "C" fn strtoul(s: *const CChar, endptr: *mut *const CChar, bas /// Rust implementation of C library function `strtoll` #[cfg_attr(feature = "strtoll", no_mangle)] -pub unsafe extern "C" fn strtoll(s: *const CChar, endptr: *mut *const CChar, base: CInt) -> CLongLong { +pub unsafe extern "C" fn strtoll( + s: *const CChar, + endptr: *mut *const CChar, + base: CInt, +) -> CLongLong { strtox(s, endptr, base, CLongLong::MIN, CLongLong::MAX as _) as CLongLong } @@ -42,13 +46,21 @@ pub unsafe extern "C" fn strtoull( /// Rust implementation of C library function `strtoimax` #[cfg_attr(feature = "strtoimax", no_mangle)] -pub unsafe extern "C" fn strtoimax(s: *const CChar, endptr: *mut *const CChar, base: CInt) -> CIntMax { +pub unsafe extern "C" fn strtoimax( + s: *const CChar, + endptr: *mut *const CChar, + base: CInt, +) -> CIntMax { strtox(s, endptr, base, CIntMax::MIN, CIntMax::MAX as _) as CIntMax } /// Rust implementation of C library function `strtoumax` #[cfg_attr(feature = "strtoumax", no_mangle)] -pub unsafe extern "C" fn strtoumax(s: *const CChar, endptr: *mut *const CChar, base: CInt) -> CUIntMax { +pub unsafe extern "C" fn strtoumax( + s: *const CChar, + endptr: *mut *const CChar, + base: CInt, +) -> CUIntMax { strtox(s, endptr, base, 0, CUIntMax::MAX) as CUIntMax } @@ -60,7 +72,7 @@ pub unsafe fn strtox( max: CUIntMax, ) -> CUIntMax { if !(0..=36).contains(&base) { - // TODO: set errno to EINVAL + // TODO: set errno to EINVAL return 0; } @@ -147,7 +159,7 @@ pub unsafe fn strtox( // Report overflow. if overflow { - // TODO: set errno to ERANGE + // TODO: set errno to ERANGE return if negate && min != 0 { min as CUIntMax } else { @@ -223,10 +235,7 @@ mod tests { unsafe { strtoul(b"0xAA123\0".as_ptr(), null_mut(), 0) }, 0xAA123 ); - assert_eq!( - unsafe { strtoul(b"0X00\0".as_ptr(), null_mut(), 0) }, - 0x00 - ); + assert_eq!(unsafe { strtoul(b"0X00\0".as_ptr(), null_mut(), 0) }, 0x00); assert_eq!( unsafe { strtoul(b"-0x123456F\0".as_ptr(), null_mut(), 0) }, (-0x123456Fi32) as _ From 25a2d8bc6353fc6186ac2ae455695043efdf13c3 Mon Sep 17 00:00:00 2001 From: Myung Gyungmin Date: Mon, 22 Jan 2024 01:38:50 +0900 Subject: [PATCH 17/20] fix build.rs --- build.rs | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/build.rs b/build.rs index 8d572c3..6c6cb75 100644 --- a/build.rs +++ b/build.rs @@ -1,36 +1,25 @@ fn main() { if cfg!(feature = "snprintf") { // Build our snprintf substitute (which has to be C as Rust doesn't do varargs) - let mut build = cc::Build::new() - .warnings(true) + let mut build = cc::Build::new(); + + build + .warnings(true) .extra_warnings(true) .flag("-std=c99") - .file("./src/snprintf.c") - .clone(); + .file("./src/snprintf.c"); - #[cfg(feature = "itoa")] - { - build = build.define("itoa", "itoa").clone(); - } - #[cfg(feature = "utoa")] - { - build = build.define("utoa", "utoa").clone(); - } - #[cfg(feature = "strtoul")] - { - build = build.define("strtoul", "strtoul").clone(); - } #[cfg(not(feature = "itoa"))] { - build = build.define("itoa", "tinyrlibc_itoa").clone(); + build.define("itoa", "tinyrlibc_itoa"); } #[cfg(not(feature = "utoa"))] { - build = build.define("utoa", "tinyrlibc_utoa").clone(); + build.define("utoa", "tinyrlibc_utoa"); } #[cfg(not(feature = "strtoul"))] { - build = build.define("strtoul", "tinyrlibc_strtoul").clone(); + build.define("strtoul", "tinyrlibc_strtoul"); } build.compile("clocal"); From d80876f9e776bf045b1b36326a639809e7190fad Mon Sep 17 00:00:00 2001 From: Myung Gyungmin Date: Mon, 22 Jan 2024 02:05:14 +0900 Subject: [PATCH 18/20] fmt --- build.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build.rs b/build.rs index 6c6cb75..57017a5 100644 --- a/build.rs +++ b/build.rs @@ -2,9 +2,9 @@ fn main() { if cfg!(feature = "snprintf") { // Build our snprintf substitute (which has to be C as Rust doesn't do varargs) let mut build = cc::Build::new(); - - build - .warnings(true) + + build + .warnings(true) .extra_warnings(true) .flag("-std=c99") .file("./src/snprintf.c"); From 188ab61a1e224ea387c70c01d888e30c0a8aa003 Mon Sep 17 00:00:00 2001 From: Myung Gyungmin Date: Mon, 22 Jan 2024 06:41:02 +0900 Subject: [PATCH 19/20] remove errno feature --- Cargo.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d381c10..cf02b37 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,6 @@ all = [ "utoa", "itoa", "snprintf", - "errno", "isspace", "isdigit", "isalpha", @@ -59,7 +58,6 @@ atoi = [] utoa = [] itoa = [] snprintf = [] -errno = [] isspace = [] isdigit = [] isalpha = [] From 66633f6aaa227021826f2a5fcd210bcd28372368 Mon Sep 17 00:00:00 2001 From: Jonathan Pallant Date: Mon, 22 Jan 2024 09:38:03 +0000 Subject: [PATCH 20/20] Update Cargo.toml - remove duplicate flag --- Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index cf02b37..241d30c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,6 @@ all = [ "strtoull", "strtoimax", "strtoumax", - "strtoul", "strstr", "strchr", "atoi",