Files
RedBear-OS/src/header/string/mod.rs
T
2018-12-02 08:16:38 -07:00

455 lines
11 KiB
Rust

//! string implementation for Redox, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/string.h.html
use core::mem;
use core::ptr;
use core::usize;
use cbitset::BitSet256;
use header::errno::*;
use header::signal;
use platform;
use platform::types::*;
#[no_mangle]
pub unsafe extern "C" fn memccpy(
dest: *mut c_void,
src: *const c_void,
c: c_int,
n: size_t,
) -> *mut c_void {
let to = memchr(src, c, n);
if to.is_null() {
return to;
}
let dist = (to as usize) - (src as usize);
if memcpy(dest, src, dist).is_null() {
return ptr::null_mut();
}
(dest as *mut u8).add(dist + 1) as *mut c_void
}
#[no_mangle]
pub unsafe extern "C" fn memchr(s: *const c_void, c: c_int, n: size_t) -> *mut c_void {
let mut s = s as *const u8;
let c = c as u8;
let mut n = n;
// read one byte at a time until s is aligned
while s as usize % mem::size_of::<usize>() != 0 {
if n == 0 {
return ptr::null_mut();
}
if *s == c {
return s as *mut c_void;
}
n -= 1;
s = s.offset(1);
}
let mut s = s as *const usize;
let lowbits = !0 as usize / 255;
let highbits = lowbits * 0x80;
let repeated_c = lowbits * c as usize;
while n >= mem::size_of::<usize>() {
// read multiple bytes at a time
// turn the requested byte into 8 zero bits
let m = *s ^ repeated_c;
// subtracting one from zero flips high bit from 0 to 1
if (m.wrapping_sub(lowbits) & !m & highbits) != 0 {
break;
}
n -= mem::size_of::<usize>();
s = s.offset(1);
}
let mut s = s as *const u8;
while n > 0 {
if *s == c {
return s as *mut c_void;
}
n -= 1;
s = s.offset(1);
}
ptr::null_mut()
}
#[no_mangle]
pub unsafe extern "C" fn memcmp(s1: *const c_void, s2: *const c_void, n: size_t) -> c_int {
let (div, rem) = (n / mem::size_of::<usize>(), n % mem::size_of::<usize>());
let mut a = s1 as *const usize;
let mut b = s2 as *const usize;
for _ in 0..div {
if *a != *b {
for i in 0..mem::size_of::<usize>() {
let c = *(a as *const u8).add(i);
let d = *(b as *const u8).add(i);
if c != d {
return c as c_int - d as c_int;
}
}
unreachable!()
}
a = a.offset(1);
b = b.offset(1);
}
let mut a = a as *const u8;
let mut b = b as *const u8;
for _ in 0..rem {
if *a != *b {
return *a as c_int - *b as c_int;
}
a = a.offset(1);
b = b.offset(1);
}
0
}
#[no_mangle]
pub unsafe extern "C" fn memcpy(s1: *mut c_void, s2: *const c_void, n: size_t) -> *mut c_void {
let mut i = 0;
while i + 7 < n {
*(s1.add(i) as *mut u64) = *(s2.add(i) as *const u64);
i += 8;
}
while i < n {
*(s1 as *mut u8).add(i) = *(s2 as *const u8).add(i);
i += 1;
}
s1
}
#[no_mangle]
pub unsafe extern "C" fn memmove(s1: *mut c_void, s2: *const c_void, n: size_t) -> *mut c_void {
if s2 < s1 as *const c_void {
// copy from end
let mut i = n;
while i != 0 {
i -= 1;
*(s1 as *mut u8).add(i) = *(s2 as *const u8).add(i);
}
} else {
// copy from beginning
let mut i = 0;
while i < n {
*(s1 as *mut u8).add(i) = *(s2 as *const u8).add(i);
i += 1;
}
}
s1
}
#[no_mangle]
pub unsafe extern "C" fn memset(s: *mut c_void, c: c_int, n: size_t) -> *mut c_void {
let mut i = 0;
while i < n {
*(s as *mut u8).add(i) = c as u8;
i += 1;
}
s
}
#[no_mangle]
pub unsafe extern "C" fn strchr(mut s: *const c_char, c: c_int) -> *mut c_char {
let c = c as c_char;
while *s != 0 {
if *s == c {
return s as *mut c_char;
}
s = s.offset(1);
}
ptr::null_mut()
}
#[no_mangle]
pub unsafe extern "C" fn strcmp(s1: *const c_char, s2: *const c_char) -> c_int {
strncmp(s1, s2, usize::MAX)
}
#[no_mangle]
pub unsafe extern "C" fn strcoll(s1: *const c_char, s2: *const c_char) -> c_int {
// relibc has no locale stuff (yet)
strcmp(s1, s2)
}
#[no_mangle]
pub unsafe extern "C" fn strcpy(dst: *mut c_char, src: *const c_char) -> *mut c_char {
let mut i = 0;
loop {
let byte = *src.offset(i);
*dst.offset(i) = byte;
if byte == 0 {
break;
}
i += 1;
}
dst
}
pub unsafe fn inner_strspn(s1: *const c_char, s2: *const c_char, cmp: bool) -> size_t {
let mut s1 = s1 as *const u8;
let mut s2 = s2 as *const u8;
// The below logic is effectively ripped from the musl implementation. It
// works by placing each byte as it's own bit in an array of numbers. Each
// number can hold up to 8 * mem::size_of::<usize>() bits. We need 256 bits
// in total, to fit one byte.
let mut set = BitSet256::new();
while *s2 != 0 {
set.insert(*s2 as usize);
s2 = s2.offset(1);
}
let mut i = 0;
while *s1 != 0 {
if set.contains(*s1 as usize) != cmp {
break;
}
i += 1;
s1 = s1.offset(1);
}
i
}
#[no_mangle]
pub unsafe extern "C" fn strcspn(s1: *const c_char, s2: *const c_char) -> size_t {
inner_strspn(s1, s2, false)
}
#[no_mangle]
pub unsafe extern "C" fn strdup(s1: *const c_char) -> *mut c_char {
strndup(s1, usize::MAX)
}
#[no_mangle]
pub unsafe extern "C" fn strndup(s1: *const c_char, size: size_t) -> *mut c_char {
let len = strnlen(s1, size);
// the "+ 1" is to account for the NUL byte
let buffer = platform::alloc(len + 1) as *mut c_char;
if buffer.is_null() {
platform::errno = ENOMEM as c_int;
} else {
//memcpy(buffer, s1, len)
let mut i = 0;
while i < len {
*buffer.add(i) = *s1.add(i);
i += 1;
}
*buffer.add(len) = 0;
}
buffer
}
#[no_mangle]
pub unsafe extern "C" fn strerror(errnum: c_int) -> *mut c_char {
use core::fmt::Write;
static mut strerror_buf: [u8; 256] = [0; 256];
let mut w = platform::StringWriter(strerror_buf.as_mut_ptr(), strerror_buf.len());
if errnum >= 0 && errnum < STR_ERROR.len() as c_int {
w.write_str(STR_ERROR[errnum as usize]);
} else {
w.write_fmt(format_args!("Unknown error {}", errnum));
}
strerror_buf.as_mut_ptr() as *mut c_char
}
#[no_mangle]
pub unsafe extern "C" fn strlen(s: *const c_char) -> size_t {
strnlen(s, usize::MAX)
}
#[no_mangle]
pub unsafe extern "C" fn strnlen(s: *const c_char, size: size_t) -> size_t {
let mut i = 0;
while i < size {
if *s.add(i) == 0 {
break;
}
i += 1;
}
i as size_t
}
#[no_mangle]
pub unsafe extern "C" fn strcat(s1: *mut c_char, s2: *const c_char) -> *mut c_char {
strncat(s1, s2, usize::MAX)
}
#[no_mangle]
pub unsafe extern "C" fn strncat(s1: *mut c_char, s2: *const c_char, n: size_t) -> *mut c_char {
let len = strlen(s1 as *const c_char);
let mut i = 0;
while i < n {
let b = *s2.add(i);
if b == 0 {
break;
}
*s1.add(len + i) = b;
i += 1;
}
*s1.add(len + i) = 0;
s1
}
#[no_mangle]
pub unsafe extern "C" fn strncmp(s1: *const c_char, s2: *const c_char, n: size_t) -> c_int {
let s1 = core::slice::from_raw_parts(s1 as *const c_uchar, n);
let s2 = core::slice::from_raw_parts(s2 as *const c_uchar, n);
for (&a, &b) in s1.iter().zip(s2.iter()) {
let val = (a as c_int) - (b as c_int);
if a != b || a == 0 {
return val;
}
}
0
}
#[no_mangle]
pub unsafe extern "C" fn strncpy(dst: *mut c_char, src: *const c_char, n: size_t) -> *mut c_char {
let mut i = 0;
while *src.add(i) != 0 && i < n {
*dst.add(i) = *src.add(i);
i += 1;
}
for i in i..n {
*dst.add(i) = 0;
}
dst
}
#[no_mangle]
pub unsafe extern "C" fn strpbrk(s1: *const c_char, s2: *const c_char) -> *mut c_char {
let p = s1.add(strcspn(s1, s2));
if *p != 0 {
p as *mut c_char
} else {
ptr::null_mut()
}
}
#[no_mangle]
pub unsafe extern "C" fn strrchr(s: *const c_char, c: c_int) -> *mut c_char {
let len = strlen(s) as isize;
let c = c as i8;
let mut i = len - 1;
while i >= 0 {
if *s.offset(i) == c {
return s.offset(i) as *mut c_char;
}
i -= 1;
}
ptr::null_mut()
}
#[no_mangle]
pub unsafe extern "C" fn strsignal(sig: c_int) -> *const c_char {
signal::_signal_strings[sig as usize].as_ptr() as *const c_char
}
#[no_mangle]
pub unsafe extern "C" fn strspn(s1: *const c_char, s2: *const c_char) -> size_t {
inner_strspn(s1, s2, true)
}
unsafe fn inner_strstr(
mut haystack: *const c_char,
needle: *const c_char,
mask: c_char,
) -> *mut c_char {
while *haystack != 0 {
let mut i = 0;
loop {
if *needle.offset(i) == 0 {
// We reached the end of the needle, everything matches this far
return haystack as *mut c_char;
}
if *haystack.offset(i) & mask != *needle.offset(i) & mask {
break;
}
i += 1;
}
haystack = haystack.offset(1);
}
ptr::null_mut()
}
#[no_mangle]
pub unsafe extern "C" fn strstr(haystack: *const c_char, needle: *const c_char) -> *mut c_char {
inner_strstr(haystack, needle, !0)
}
#[no_mangle]
pub unsafe extern "C" fn strcasestr(haystack: *const c_char, needle: *const c_char) -> *mut c_char {
inner_strstr(haystack, needle, !32)
}
#[no_mangle]
pub unsafe extern "C" fn strtok(s1: *mut c_char, delimiter: *const c_char) -> *mut c_char {
static mut HAYSTACK: *mut c_char = ptr::null_mut();
strtok_r(s1, delimiter, &mut HAYSTACK)
}
#[no_mangle]
pub unsafe extern "C" fn strtok_r(
s: *mut c_char,
delimiter: *const c_char,
lasts: *mut *mut c_char,
) -> *mut c_char {
// Loosely based on GLIBC implementation
let mut haystack = s;
if haystack.is_null() {
if (*lasts).is_null() {
return ptr::null_mut();
}
haystack = *lasts;
}
// Skip past any extra delimiter left over from previous call
haystack = haystack.add(strspn(haystack, delimiter));
if *haystack == 0 {
*lasts = ptr::null_mut();
return ptr::null_mut();
}
// Build token by injecting null byte into delimiter
let token = haystack;
haystack = strpbrk(token, delimiter);
if !haystack.is_null() {
haystack.write(0);
haystack = haystack.add(1);
*lasts = haystack;
} else {
*lasts = ptr::null_mut();
}
token
}
#[no_mangle]
pub unsafe extern "C" fn strxfrm(s1: *mut c_char, s2: *const c_char, n: size_t) -> size_t {
// relibc has no locale stuff (yet)
let len = strlen(s2);
if len < n {
strcpy(s1, s2);
}
len
}