https://github.com/lrs-lang/lib
An experimental standard library
https://github.com/lrs-lang/lib
Last synced: 8 months ago
JSON representation
An experimental standard library
- Host: GitHub
- URL: https://github.com/lrs-lang/lib
- Owner: lrs-lang
- License: mpl-2.0
- Created: 2015-03-19T01:37:21.000Z (almost 11 years ago)
- Default Branch: master
- Last Pushed: 2016-04-15T13:52:11.000Z (over 9 years ago)
- Last Synced: 2024-11-14T17:47:33.664Z (about 1 year ago)
- Language: Rust
- Homepage:
- Size: 1.67 MB
- Stars: 225
- Watchers: 13
- Forks: 3
- Open Issues: 28
-
Metadata Files:
- Readme: README.adoc
- License: LICENSE
Awesome Lists containing this project
- awesome-list - lib - lang | 204 | (Rust)
README
= *lrs*
:toc: macro
ifdef::env-github[:build_link: link:Documentation/adoc/building_and_using.adoc]
ifndef::env-github[:build_link: link:Documentation/html/building_and_using.html]
:logo: assets/logo.png
:source-language: rust
image::{logo}[logo,float="left"]
lrs is a highly experimental, linux-only standard library for the rustc
compiler. It does not use any parts of the standard library that is part of the
rust distribution.
'''
toc::[]
== Features
Since lrs is based on the rust compiler, it shares many features with rust
(e.g., lifetimes, borrow checking, integer overflow checking, etc.) But rustc
allows us to make significant changes to the language as long as we don't use
Mozilla's standard library. This section lists some of the differences between
rust and lrs and other features of lrs.
NOTE: In this section we'll compare programs compiled against lrs and programs
compiled against the "standard" standard library that comes with the rust
distribution. To make things simpler, we will call programs that use lrs "lrs
programs" and programs that use Mozilla's standard library "rust programs". It
should be clear from the context what is meant.
=== No unwinding
Unwinding and the `panic` macro have been removed from lrs. This means that
error handling works via return values or--in the case of unrecoverable
errors--process termination. This has the following advantages:
Potentially better performance:: Consider the following function:
+
[source]
----
fn f(a: &mut u8, b: &mut u8, g: fn()) {
mem::swap(a, b);
g();
mem::swap(a, b);
}
----
+
If `g` cannot unwind, then this function can be optimized by removing both
`swap` calls. But if `g` can unwind, then the `swap` calls must stay in place
since destructors called during unwinding might access `a` and `b`.
No exception unsafety:: Consider the following (incorrect) rust code:
+
[source]
----
fn push(a: &mut Vec, g: fn() -> T) {
unsafe {
assert!(a.capacity() > a.len());
let len = a.len();
a.set_len(len + 1); // <-- BUG
ptr::write(a.as_mut_ptr().offset(len as isize), g());
}
}
----
+
This is a naive implementation of a non-allocating `push` method on `Vec`.
The code is incorrect because the length of the vector is increased before the
return value of `g` has been written to it. If `g` unwinds, the destructor of
`Vec` will access the invalid value at `a[len]`, which is likely undefined
behavior. This problem does not exist in lrs. See
https://github.com/rust-lang/rfcs/pull/1236[this] (long) thread for a discussion
of exception safety in rust.
=== Small executables
:calc_url: http://is.gd/Ep2KIi
lrs programs usually compile down to executable with a size comparable to that
of equivalent C programs.
In the table below, `lrs + musl` denotes programs that were statically compiled
against musl, and `lrs - libc` denotes programs that don't depend on a libc.
|===
|Name |lrs + glibc |lrs + musl |lrs - libc |C (glibc) |rust
|Hello World |7.0KB |4.0KB |1.3KB |6.5KB |436KB
|http://pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html[test]
|18KB
|21KB
|n/a
|35KB
|462KB
|{calc_url}[A calculator]
|9.2KB
|5.8KB
|n/a
|n/a
|437KB
|===
NOTE: All programs were compiled with the `-O -C lto` flags.
=== Direct system calls
lrs interacts with the kernel directly through system calls. That is, lrs does
not depend on a libc for 99% of the work. This allows us to use kernel features
that do not (yet) have an equivalent libc function and removes an unnecessary
layer of abstraction.
It is, in fact, possible to use lrs without a libc. However, this mode is not
yet fully developed and mostly useful for testing cross-compilation.
=== glibc and musl support
Due to what was discussed in the previous section, the lrs/libc interface is so
small that it's almost trivial to make lrs work with different libc versions.
Currently, lrs is known to work with glibc and musl.
=== Portable
Even though lrs avoids the libc abstraction layer, porting lrs to new linux
platforms is easy. This is due to the way the platform dependent parts of lrs
mirror the equivalent parts in the linux kernel source code. lrs has already
been ported to *x86_64*, *x32*, *i686*, *arm*, and *arm64*.
WARNING: lrs has only been tested on *x86_64*.
=== Per-object allocators
Many allocating structures in lrs (such as vectors, strings, hashmaps) come
with an optional allocator argument. The following allocators are part of lrs:
Libc:: Uses `malloc` and friends from the libc.
JeMalloc:: Uses jemalloc's non-standard API with sized allocations and
deallocations for higher efficiency.
NoMem:: This dummy-allocator always reports an out-of-memory condition.
Bda:: The *brain-dead allocator* only allocates in multiples of the page size.
This is very useful for applications that have few allocations whose size is
unknown at compile time and can rapidly increase.
Careful note should be taken of the *NoMem* allocator. Consider the following
code:
[source]
----
let mut buf = [0; 20];
let mut vec = Vec::buffered(&mut buf);
write!(&mut vec, "Hello World {}", 10).unwrap();
assert!(&*vec == "Hello World 10");
----
The vector is backed by the *NoMem* allocator and the buffer declared in the
first line. It will never dynamically allocate any memory. If we were to write
more bytes than can be stored in the buffer, `write!` would return that the
vector is out of memory. Using this feature, lrs often allows the user to avoid
allocations in cases where doing so would be rather inconvenient in rust.
Nevertheless, it's easy to use lrs collections in the common case where the user
does not care about dynamic allocations. This is because all collections declare
a default allocator so that `Vec` is the same as `Vec`. This default
allocator can be chosen at compile time.
=== No allocations in the 99.9% case
All APIs are designed to not allocate memory in the common case. For example,
`File::open` will only allocate memory if the requested path is longer than
`PATH_MAX`. In those cases the API uses the so called fallback allocator. If
the user does not want memory to be allocated in those exceptional situations,
they can disable said allocator at compile time.
=== Fast compilation
lrs is split into many small crates and provides incremental compilation
independent of the rustc compiler. Compiling a single crate during development
often takes less than a second. To this end, lrs comes with its own
build system--lrs_build--which ensures that only the minimal amount of work is
done by the compiler.
Furthermore, even complete builds do not take very long. On this (old) machine,
a complete build takes 28 seconds without optimization and 41 seconds with
optimization.
=== Extensive Linux API coverage
:syscalls: http://lrs-lang.github.io/pages/doc/lrs::syscall.html
:file: http://lrs-lang.github.io/pages/doc/lrs::file::File.html
lrs already wraps many of the commonly used linux system calls. See
{syscalls}[this] page of the lrs documentation for a list of mostly safe system
call wrappers. But note that most of these functions have much nicer wrappers in
other parts of the library.
For example, the {file}[File] struct exposes many syscalls that modify files.
=== Easy to use
Even though lrs programs don't use the standard library that comes with the
compiler, the user doesn't have to bother with annoying annotations. For
example, the following lrs program can be compiled as written:
[source]
----
use std::tty::{is_a_tty};
fn main() {
if is_a_tty(&1) {
println!("stdout is a tty");
} else {
println!("stdout is not a tty");
}
}
----
This is because lrs comes with its own compiler driver that takes care of
injecting lrs instead of rust.
== Building and Using
Please see the detailed {build_link}[Building and Using] guide.
== Examples
:lrs_build: https://github.com/lrs-lang/build
:examples: https://github.com/lrs-lang/examples
See {lrs_build}[lrs_build] for an example of an lrs program. Many more examples
can be found in the {examples}[examples] repo. But note that those examples have
not yet been organized and documented properly.
== Documentation
:doc: https://github.com/lrs-lang/doc
:docs: http://lrs-lang.github.io/pages/doc/lrs.html
Documentation regarding lrs in general can be found in the link:Documentation[]
directory.
Documentation of the library is generated via {doc}[lrs_doc]. An outdated
version of said documentation can be found {docs}[here].
== License
:license: link:LICENSE
The whole library is licensed under the {license}[MPL 2.0] license which
allows static linking into proprietary programs. It is copy-left on a
file-by-file basis: Changes to files licensed under the MPL 2.0 have to be
distributed under the same license. It also allows the code to be freely used
under several (L)GPL licenses.
Some other parts--such as the compiler plugin and the compiler driver--are
licensed under the MIT license.
== Logo
:simple-linux-logo: http://dablim.deviantart.com/art/Simple-Linux-Logo-336131202
:dablim: http://dablim.deviantart.com/
:ccby: http://creativecommons.org/licenses/by-sa/4.0/
The lrs link:{logo}[logo] shows a penguin in a sprocket.
It is based on {simple-linux-logo}[Simple Linux Logo] by {dablim}[Dablim] and is
licensed under {ccby}[CC BY-SA 4.0].