Skip to content

Commit 6ce526e

Browse files
committed
First basic implementation of a few common functions.
0 parents  commit 6ce526e

File tree

11 files changed

+419
-0
lines changed

11 files changed

+419
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/target
2+
**/*.rs.bk
3+
Cargo.lock

Cargo.toml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[package]
2+
name = "tinyrlibc"
3+
version = "0.1.0"
4+
authors = ["Jonathan 'theJPster' Pallant <[email protected]>"]
5+
edition = "2018"
6+
description = "Tiny, incomplete C library for bare-metal targets, written in Stable (but Unsafe) Rust"
7+
license-file = "LICENCES.md"
8+
readme = "README.md"
9+
10+
[dependencies]

LICENCES.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Blue Oak Model License
2+
3+
Version 1.0.0
4+
5+
## Purpose
6+
7+
This license gives everyone as much permission to work with
8+
this software as possible, while protecting contributors
9+
from liability.
10+
11+
## Acceptance
12+
13+
In order to receive this license, you must agree to its
14+
rules. The rules of this license are both obligations
15+
under that agreement and conditions to your license.
16+
You must not do anything with this software that triggers
17+
a rule that you cannot or will not follow.
18+
19+
## Copyright
20+
21+
Each contributor licenses you to do everything with this
22+
software that would otherwise infringe that contributor's
23+
copyright in it.
24+
25+
## Notices
26+
27+
You must ensure that everyone who gets a copy of
28+
any part of this software from you, with or without
29+
changes, also gets the text of this license or a link to
30+
<https://blueoakcouncil.org/license/1.0.0>.
31+
32+
## Excuse
33+
34+
If anyone notifies you in writing that you have not
35+
complied with [Notices](#notices), you can keep your
36+
license by taking all practical steps to comply within 30
37+
days after the notice. If you do not do so, your license
38+
ends immediately.
39+
40+
## Patent
41+
42+
Each contributor licenses you to do everything with this
43+
software that would otherwise infringe any patent claims
44+
they can license or become able to license.
45+
46+
## Reliability
47+
48+
No contributor can revoke this license.
49+
50+
## No Liability
51+
52+
***As far as the law allows, this software comes as is,
53+
without any warranty or condition, and no contributor
54+
will be liable to anyone for any damages related to this
55+
software or this license, under any kind of legal claim.***

README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Tiny Rust libc
2+
3+
## Introduction
4+
5+
This is a _tiny_ libc implementation, mostly (but not entirely) written in the Rust programming language. It is useful for bare-metal embedded Rust applications that need a C library (maybe because of some third-party library written in C they want to use) but don't want to link against a full [newlib](https://sourceware.org/newlib), or who tried but had trouble with both newlib and [compiler_builtins](https://github.com/rust-lang-nursery/compiler-builtins) defining symbols like `memset`.
6+
7+
This crate basically came about so that the [nrfxlib](https://github.com/NordicPlayground/nrfxlib) binary interface library for the nRF9160 would work with Rust.
8+
9+
## Implemented so far
10+
11+
* strol
12+
13+
## To Do
14+
15+
* Anything else nrfxlib needs
16+
* Anything anyone is prepared to submit
17+
18+
## Licence
19+
20+
As this is going to be a bunch of bits taken from all over the place (some newlib, some relibc, etc), each function has its own file and each file has its own licence. Any new licences should be appended to the [LICENCE.md](./LICENCE.md) file.
21+

src/atoi.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//! Rust implementation of C library function `atoi`
2+
//!
3+
//! Copyright (c) Jonathan 'theJPster' Pallant 2019
4+
//! Licensed under the Blue Oak Model Licence 1.0.0
5+
6+
use crate::strtol;
7+
8+
/// Converts a null-terminated string representing a decimal integer, into an
9+
/// integer. No indication of error.
10+
///
11+
/// ```
12+
/// use tinyrlibc::atoi;
13+
/// assert_eq!(unsafe { atoi(b"123".as_ptr()) }, 123);
14+
/// assert_eq!(unsafe { atoi(b"123x".as_ptr()) }, 123);
15+
/// assert_eq!(unsafe { atoi(b"".as_ptr()) }, 0);
16+
/// ```
17+
#[no_mangle]
18+
pub unsafe extern "C" fn atoi(s: *const crate::CChar) -> crate::CInt {
19+
let result = strtol(s);
20+
if result > crate::CInt::max_value() {
21+
crate::CInt::max_value()
22+
} else if result < crate::CInt::min_value() {
23+
crate::CInt::min_value()
24+
} else {
25+
result as crate::CInt
26+
}
27+
}

src/lib.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
//! A tiny C library, written in Rust.
2+
//!
3+
//! See README.md for more details.
4+
//!
5+
//! This file is Copyright (c) Jonathan 'theJPster' Pallant 2019
6+
//! Licensed under the Blue Oak Model Licence 1.0.0
7+
//!
8+
//! See each module for its respective licence.
9+
10+
#![cfg_attr(not(test), no_std)]
11+
12+
#[cfg(test)]
13+
#[allow(unused_imports)]
14+
use std as core;
15+
16+
mod strcmp;
17+
pub use self::strcmp::strcmp;
18+
19+
mod strncmp;
20+
pub use self::strncmp::strncmp;
21+
22+
mod strlen;
23+
pub use self::strlen::strlen;
24+
25+
mod strtol;
26+
pub use self::strtol::strtol;
27+
28+
mod strstr;
29+
pub use self::strstr::strstr;
30+
31+
mod atoi;
32+
pub use self::atoi::atoi;
33+
34+
// TODO: Add cfg defines / work these out for platforms other than armv6/7/8m
35+
36+
pub type CLongLong = i64;
37+
pub type CLong = i32;
38+
pub type CInt = i32;
39+
pub type CChar = u8;
40+
41+
/// This allows you to iterate a null-terminated string in a relatively simple
42+
/// way.
43+
pub struct CStringIter {
44+
ptr: *const CChar,
45+
idx: isize,
46+
}
47+
48+
impl CStringIter {
49+
/// Create a new iterator from a pointer to a null-terminated string. The
50+
/// behaviour is undefined if the string is not null-terminated.
51+
pub fn new(s: *const CChar) -> CStringIter {
52+
CStringIter { ptr: s, idx: 0 }
53+
}
54+
}
55+
56+
impl core::iter::Iterator for CStringIter {
57+
type Item = CChar;
58+
fn next(&mut self) -> Option<Self::Item> {
59+
let c = unsafe { *self.ptr.offset(self.idx) };
60+
if c == 0 {
61+
None
62+
} else {
63+
self.idx += 1;
64+
Some(c)
65+
}
66+
}
67+
}

src/strcmp.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//! Rust implementation of C library function `strncmp`
2+
//!
3+
//! Copyright (c) Jonathan 'theJPster' Pallant 2019
4+
//! Licensed under the Blue Oak Model Licence 1.0.0
5+
6+
/// Rust implementation of C library function `strcmp`
7+
#[no_mangle]
8+
pub unsafe extern "C" fn strcmp(s1: *const crate::CChar, s2: *const crate::CChar) -> crate::CInt {
9+
for i in 0.. {
10+
let s1_i = s1.offset(i);
11+
let s2_i = s2.offset(i);
12+
13+
let val = *s1_i as i32 - *s2_i as i32;
14+
if val != 0 || *s1_i == 0 {
15+
return i32::from(val);
16+
}
17+
}
18+
0
19+
}

src/strlen.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//! Rust implementation of C library function `strlen`
2+
//!
3+
//! Copyright (c) Jonathan 'theJPster' Pallant 2019
4+
//! Licensed under the Blue Oak Model Licence 1.0.0
5+
6+
/// Rust implementation of C library function `strlen`
7+
#[no_mangle]
8+
pub unsafe extern "C" fn strlen(mut s: *const crate::CChar) -> usize {
9+
let mut result = 0;
10+
while *s != 0 {
11+
s = s.offset(1);
12+
result += 1;
13+
}
14+
result
15+
}
16+
17+
#[cfg(test)]
18+
mod test {
19+
use super::*;
20+
21+
#[test]
22+
fn test1() {
23+
assert_eq!(unsafe { strlen(b"Hello\0" as *const crate::CChar) }, 5);
24+
}
25+
26+
#[test]
27+
fn test2() {
28+
assert_eq!(unsafe { strlen(b"\0" as *const crate::CChar) }, 0);
29+
}
30+
31+
#[test]
32+
fn test3() {
33+
assert_eq!(unsafe { strlen(b"X\0" as *const crate::CChar) }, 1);
34+
}
35+
36+
}

src/strncmp.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
//! Rust implementation of C library function `strncmp`
2+
//!
3+
//! Copyright (c) Jonathan 'theJPster' Pallant 2019
4+
//! Licensed under the Blue Oak Model Licence 1.0.0
5+
6+
/// Rust implementation of C library function `strncmp`. Passing NULL
7+
/// (core::ptr::null()) gives undefined behaviour.
8+
#[no_mangle]
9+
pub unsafe extern "C" fn strncmp(
10+
s1: *const crate::CChar,
11+
s2: *const crate::CChar,
12+
n: usize,
13+
) -> crate::CInt {
14+
for i in 0..n as isize {
15+
let s1_i = s1.offset(i);
16+
let s2_i = s2.offset(i);
17+
18+
let val = *s1_i as i32 - *s2_i as i32;
19+
if val != 0 || *s1_i == 0 {
20+
return i32::from(val);
21+
}
22+
}
23+
0
24+
}
25+
26+
#[cfg(test)]
27+
mod test {
28+
use super::*;
29+
30+
#[test]
31+
fn matches() {
32+
let a = b"123\0";
33+
let b = b"1234\0";
34+
let result = unsafe { strncmp(a.as_ptr(), b.as_ptr(), 3) };
35+
// Match!
36+
assert_eq!(result, 0);
37+
}
38+
39+
#[test]
40+
fn no_match() {
41+
let a = b"123\0";
42+
let b = b"x1234\0";
43+
let result = unsafe { strncmp(a.as_ptr(), b.as_ptr(), 3) };
44+
// No match, first string first
45+
assert!(result < 0);
46+
}
47+
48+
#[test]
49+
fn no_match2() {
50+
let a = b"bbbbb\0";
51+
let b = b"aaaaa\0";
52+
let result = unsafe { strncmp(a.as_ptr(), b.as_ptr(), 3) };
53+
// No match, second string first
54+
assert!(result > 0);
55+
}
56+
}

src/strstr.rs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
//! Rust implementation of C library function `strstr`
2+
//!
3+
//! Copyright (c) Jonathan 'theJPster' Pallant 2019
4+
//! Licensed under the Blue Oak Model Licence 1.0.0
5+
6+
use crate::CStringIter;
7+
8+
/// Rust implementation of C library function `strstr`
9+
#[no_mangle]
10+
pub unsafe extern "C" fn strstr(
11+
haystack: *const crate::CChar,
12+
needle: *const crate::CChar,
13+
) -> *const crate::CChar {
14+
if *needle.offset(0) == 0 {
15+
return haystack;
16+
}
17+
for haystack_trim in (0..).map(|idx| haystack.offset(idx)) {
18+
if *haystack_trim == 0 {
19+
break;
20+
}
21+
let mut len = 0;
22+
for (inner_idx, nec) in CStringIter::new(needle).enumerate() {
23+
let hsc = *haystack_trim.offset(inner_idx as isize);
24+
if hsc != nec {
25+
break;
26+
}
27+
len += 1;
28+
}
29+
if *needle.offset(len) == 0 {
30+
return haystack_trim;
31+
}
32+
}
33+
core::ptr::null()
34+
}
35+
36+
#[cfg(test)]
37+
mod test {
38+
use super::*;
39+
40+
#[test]
41+
fn no_match() {
42+
let needle = b"needle\0".as_ptr();
43+
let haystack = b"haystack\0".as_ptr();
44+
let result = unsafe { strstr(haystack, needle) };
45+
assert_eq!(result, core::ptr::null());
46+
}
47+
48+
#[test]
49+
fn start() {
50+
let needle = b"hay\0".as_ptr();
51+
let haystack = b"haystack\0".as_ptr();
52+
let result = unsafe { strstr(haystack, needle) };
53+
assert_eq!(result, haystack);
54+
}
55+
56+
#[test]
57+
fn middle() {
58+
let needle = b"yst\0".as_ptr();
59+
let haystack = b"haystack\0".as_ptr();
60+
let result = unsafe { strstr(haystack, needle) };
61+
assert_eq!(result, unsafe { haystack.offset(2) });
62+
}
63+
64+
#[test]
65+
fn end() {
66+
let needle = b"stack\0".as_ptr();
67+
let haystack = b"haystack\0".as_ptr();
68+
let result = unsafe { strstr(haystack, needle) };
69+
assert_eq!(result, unsafe { haystack.offset(3) });
70+
}
71+
72+
#[test]
73+
fn partial() {
74+
let needle = b"haystacka\0".as_ptr();
75+
let haystack = b"haystack\0".as_ptr();
76+
let result = unsafe { strstr(haystack, needle) };
77+
assert_eq!(result, core::ptr::null());
78+
}
79+
}

0 commit comments

Comments
 (0)