How to convert a Rust integer type to its string representation without allocating a String? [duplicate] - io

I want to do something like:
let x = 123;
let mut buf = [0 as u8; 20];
format_to!(x --> buf);
assert_eq!(&buf[..3], &b"123"[..]);
With #![no_std] and without any memory allocator.
As I understand, there is an implementation of core::fmt::Display for u64, and I want to use it if possible.
In other words, I want to do something like format!(...), but without a memory allocator. How can I do this?

Let's start with the standard version:
use std::io::Write;
fn main() {
let x = 123;
let mut buf = [0 as u8; 20];
write!(&mut buf[..], "{}", x).expect("Can't write");
assert_eq!(&buf[0..3], b"123");
}
If we then remove the standard library:
#![feature(lang_items)]
#![no_std]
use core::panic::PanicInfo;
#[lang = "eh_personality"]
extern "C" fn eh_personality() {}
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
loop {}
}
fn main() {
let x = 123;
let mut buf = [0 as u8; 20];
write!(&mut buf[..], "{}", x).expect("Can't write");
assert_eq!(&buf[0..3], b"123");
}
We get the error
error[E0599]: no method named `write_fmt` found for type `&mut [u8]` in the current scope
--> src/main.rs:17:5
|
17 | write!(&mut buf[..], "{}", x).expect("Can't write");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
write_fmt is implemented in the core library by core::fmt::Write. If we implement it ourselves, we are able to pass that error:
#![feature(lang_items)]
#![feature(start)]
#![no_std]
use core::panic::PanicInfo;
#[lang = "eh_personality"]
extern "C" fn eh_personality() {}
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
loop {}
}
use core::fmt::{self, Write};
struct Wrapper<'a> {
buf: &'a mut [u8],
offset: usize,
}
impl<'a> Wrapper<'a> {
fn new(buf: &'a mut [u8]) -> Self {
Wrapper {
buf: buf,
offset: 0,
}
}
}
impl<'a> fmt::Write for Wrapper<'a> {
fn write_str(&mut self, s: &str) -> fmt::Result {
let bytes = s.as_bytes();
// Skip over already-copied data
let remainder = &mut self.buf[self.offset..];
// Check if there is space remaining (return error instead of panicking)
if remainder.len() < bytes.len() { return Err(core::fmt::Error); }
// Make the two slices the same length
let remainder = &mut remainder[..bytes.len()];
// Copy
remainder.copy_from_slice(bytes);
// Update offset to avoid overwriting
self.offset += bytes.len();
Ok(())
}
}
#[start]
fn start(_argc: isize, _argv: *const *const u8) -> isize {
let x = 123;
let mut buf = [0 as u8; 20];
write!(Wrapper::new(&mut buf), "{}", x).expect("Can't write");
assert_eq!(&buf[0..3], b"123");
0
}
Note that we are duplicating the behavior of io::Cursor into this wrapper. Normally, multiple writes to a &mut [u8] will overwrite each other. This is good for reusing allocation, but not useful when you have consecutive writes of the same data.
Then it's just a matter of writing a macro if you want to.
You should also be able to use a crate like arrayvec, which has written this code for you. This is untested:
#![feature(lang_items)]
#![feature(start)]
#![no_std]
use core::panic::PanicInfo;
#[lang = "eh_personality"]
extern "C" fn eh_personality() {}
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
loop {}
}
use arrayvec::ArrayString; // 0.4.10
use core::fmt::Write;
#[start]
fn start(_argc: isize, _argv: *const *const u8) -> isize {
let x = 123;
let mut buf = ArrayString::<[u8; 20]>::new();
write!(&mut buf, "{}", x).expect("Can't write");
assert_eq!(&buf, "123");
0
}

With bare_io:
use bare_io::{Cursor, Write};
let mut buf = [0 as u8; 256];
let mut cur = Cursor::new(&mut buf[..]);
write!(&mut cur, "hello world, stack buf, {}\n\0", 234).expect("!write");
unsafe { puts(buf.as_ptr()) };
With bare_io, smallvec and alloc:
use smallvec::{Array, SmallVec};
struct WriteSmallVec<A: Array<Item = u8>>(SmallVec<A>);
impl<A: Array<Item = u8>> Write for WriteSmallVec<A> {
fn write(&mut self, buf: &[u8]) -> bare_io::Result<usize> {
self.0.extend_from_slice(buf);
Ok(buf.len())
}
fn flush(&mut self) -> bare_io::Result<()> {
Ok(())
}
}
let mut sv = WriteSmallVec(SmallVec::<[u8; 256]>::new());
write!(&mut sv, "hello world, SmallVec, prev len: {}\n\0", len).expect("!write");
unsafe { puts(sv.0.as_ptr()) };
With bare_io, patched inlinable_string and alloc:
use core::fmt::Write;
use inlinable_string::{InlinableString, StringExt};
let mut is = InlinableString::new();
write!(&mut is, "hello world, InlinableString, {}\n\0", 345).expect("!write");
unsafe { puts(is.as_ptr()) };
Tested in Linux kernel,
cargo build --release -Z build-std=core,alloc --target=x86_64-linux-kernel
Also did some benchmarks, comparing a simple array with SmallVec and InlinableString: https://gitlab.com/artemciy/lin-socks/-/blob/95d2bb96/bench/stack-string.rs
p.s. bare-io has been yanked though.

Related

using C function that takes pointer to C struct typedef in Rust

I'm trying to use bindings generated for cuBLAS using bindgen. Here's what my code looks like:
mod tests {
use super::*;
#[test]
pub fn alpha () {
let mut handle: cublasHandle_t;
let mut stat: cublasStatus_t;
let mut cudaStat: cudaError_t;
... some stuff
unsafe {
cudaStat = cudaMalloc(a.as_mut_ptr() as *mut *mut c_void, a.len() as u64);
cudaStat = cudaMalloc(b.as_mut_ptr() as *mut *mut c_void, b.len() as u64);
cudaStat = cudaMalloc(c.as_mut_ptr() as *mut *mut c_void, c.len() as u64);
stat = cublasCreate_v2(handle as *mut *mut cublasContext);
}
...some stuff
}
}
I get this error:
error: expected expression, found keyword `mut`
--> src/lib.rs:44:37
|
44 | stat = cublasCreate_v2(handle as *mut *mut cublasContext);
| ^^^ expected expression
error: could not compile `cublas-rs` due to previous error
NOTE: cublasHandle_t is a typedef for *mut cublasContext.
I've tried doing just &handle, *mut handle, etc but no dice.
cublasHandle_t is only supposed to be initialized by cublasCreate_v2.
Here's what things look like in bindings.rs:
// cublasContext struct we want to pass to cublasCreate_v2
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct cublasContext {
_unused: [u8; 0],
}
// alternative typedef used by cublas
pub type cublasHandle_t = *mut cublasContext;
// function to create a cublas handle
extern "C" {
pub fn cublasCreate_v2(handle: *mut cublasHandle_t) -> cublasStatus_t;
}
I've tried initializing it like this:
let mut handle: cublasHandle_t = *mut cublasContext { _unused: [] }; // no luck
let mut handle: cublasHandle_t = cublasContext { _unused: [] } as *mut cublasContext; // no
How do I call a function like this?
Actually, you probably want to just set handle to a null pointer to start with, since you say cublasCreate_v2 is supposed to create the handle.
mod tests {
use super::*;
pub fn alpha () {
// initialize with null pointer
let mut handle: cublasHandle_t = std::ptr::null_mut();
let mut stat: cublasStatus_t;
// ... some stuff
unsafe {
cudaStat = cudaMalloc(a.as_mut_ptr() as *mut *mut c_void, a.len() as u64);
cudaStat = cudaMalloc(b.as_mut_ptr() as *mut *mut c_void, b.len() as u64);
cudaStat = cudaMalloc(c.as_mut_ptr() as *mut *mut c_void, c.len() as u64);
// pass pointer to the pointer, using `&mut x`
stat = cublasCreate_v2(&mut handle);
}
// ...some stuff
}
}
You need to create the context as a variable first, before creating a pointer to it. To create a pointer to a value, you use &mut value, similar to &value in C.
mod tests {
use super::*;
pub fn alpha () {
let mut context = cublasContext { _unused: [] }; // create context
// get pointer to context, `&mut x` can be assigned to `*mut x`
let mut handle: cublasHandle_t = &mut context;
let mut stat: cublasStatus_t;
// ... some stuff
unsafe {
cudaStat = cudaMalloc(a.as_mut_ptr() as *mut *mut c_void, a.len() as u64);
cudaStat = cudaMalloc(b.as_mut_ptr() as *mut *mut c_void, b.len() as u64);
cudaStat = cudaMalloc(c.as_mut_ptr() as *mut *mut c_void, c.len() as u64);
// pass double-pointer, using `&mut x` again
stat = cublasCreate_v2(&mut handle);
}
// ...some stuff
}
}
You might want to add #[derive(Default)] to cublasContext, so you can do cublasContext::default() instead of needing to set up _unused.

How can I use the format! macro in a no_std environment?

How could I implement the following example without using std?
let text = format!("example {:.1} test {:x} words {}", num1, num2, num3);
text has type &str and num1, num2 and num3 have any numeric type.
I've tried using numtoa and itoa/dtoa for displaying numbers but numtoa does not support floats and itoa does not support no_std. I feel like displaying a number in a string is fairly common and that I'm probably missing something obvious.
In addition to Shepmaster's answer you can also format strings without an allocator.
In core::fmt::Write you only need to implement write_str and then you get write_fmt for free.
With format_args!(...) (same syntax as format!) you can prepare a core::fmt::Arguments value, which can be passed to core::fmt::write.
See Playground:
#![crate_type = "dylib"]
#![no_std]
pub mod write_to {
use core::cmp::min;
use core::fmt;
pub struct WriteTo<'a> {
buffer: &'a mut [u8],
// on write error (i.e. not enough space in buffer) this grows beyond
// `buffer.len()`.
used: usize,
}
impl<'a> WriteTo<'a> {
pub fn new(buffer: &'a mut [u8]) -> Self {
WriteTo { buffer, used: 0 }
}
pub fn as_str(self) -> Option<&'a str> {
if self.used <= self.buffer.len() {
// only successful concats of str - must be a valid str.
use core::str::from_utf8_unchecked;
Some(unsafe { from_utf8_unchecked(&self.buffer[..self.used]) })
} else {
None
}
}
}
impl<'a> fmt::Write for WriteTo<'a> {
fn write_str(&mut self, s: &str) -> fmt::Result {
if self.used > self.buffer.len() {
return Err(fmt::Error);
}
let remaining_buf = &mut self.buffer[self.used..];
let raw_s = s.as_bytes();
let write_num = min(raw_s.len(), remaining_buf.len());
remaining_buf[..write_num].copy_from_slice(&raw_s[..write_num]);
self.used += raw_s.len();
if write_num < raw_s.len() {
Err(fmt::Error)
} else {
Ok(())
}
}
}
pub fn show<'a>(buffer: &'a mut [u8], args: fmt::Arguments) -> Result<&'a str, fmt::Error> {
let mut w = WriteTo::new(buffer);
fmt::write(&mut w, args)?;
w.as_str().ok_or(fmt::Error)
}
}
pub fn test() {
let mut buf = [0u8; 64];
let _s: &str = write_to::show(
&mut buf,
format_args!("write some stuff {:?}: {}", "foo", 42),
).unwrap();
}
In general, you don't. format! allocates a String, and a no_std environment doesn't have an allocator.
If you do have an allocator, you can use the alloc crate. This crate contains the format! macro.
#![crate_type = "dylib"]
#![no_std]
#[macro_use]
extern crate alloc;
fn thing() {
let text = format!("example {:.1} test {:x} words {}", 1, 2, 3);
}
See also:
How to format output to a byte array with no_std and no allocator?
You can also combine the usage of numtoa and arrayvec crates. Example:
#![no_std]
use numtoa::NumToA;
use arrayvec::ArrayString;
fn main() -> ! {
let mut num_buffer = [0u8; 20];
let mut text = ArrayString::<[_; 100]>::new();
let num1 = 123;
let num2 = 456;
let num3 = 789;
// text.clear(); (on subsequent usages)
text.push_str("example ");
text.push_str(num1.numtoa_str(10, &mut num_buffer));
text.push_str(" test ");
text.push_str(num2.numtoa_str(10, &mut num_buffer));
text.push_str(" words ");
text.push_str(num3.numtoa_str(10, &mut num_buffer));
}
Note that push_str can panic. Check out the api for try_ -methods
And Cargo.toml
 [dependencies]
arrayvec = { version = "0.5", default-features = false }
numtoa = "0.2"
Write a formatter!
use core::fmt::{self, Write};
use core::str;
fn main() {
// For LCD 160 / 8 = 20 chars
let mut buf = [0u8; 20];
let mut buf = ByteMutWriter::new(&mut buf[..]);
buf.clear();
write!(&mut buf, "Hello {}!", "Rust").unwrap();
// buf.as_str()
}
pub struct ByteMutWriter<'a> {
buf: &'a mut [u8],
cursor: usize,
}
impl<'a> ByteMutWriter<'a> {
pub fn new(buf: &'a mut [u8]) -> Self {
ByteMutWriter { buf, cursor: 0 }
}
pub fn as_str(&self) -> &str {
str::from_utf8(&self.buf[0..self.cursor]).unwrap()
}
#[inline]
pub fn capacity(&self) -> usize {
self.buf.len()
}
pub fn clear(&mut self) {
self.cursor = 0;
}
pub fn len(&self) -> usize {
self.cursor
}
pub fn empty(&self) -> bool {
self.cursor == 0
}
pub fn full(&self) -> bool {
self.capacity() == self.cursor
}
}
impl fmt::Write for ByteMutWriter<'_> {
fn write_str(&mut self, s: &str) -> fmt::Result {
let cap = self.capacity();
for (i, &b) in self.buf[self.cursor..cap]
.iter_mut()
.zip(s.as_bytes().iter())
{
*i = b;
}
self.cursor = usize::min(cap, self.cursor + s.as_bytes().len());
Ok(())
}
}
I build a small crate based on Shepmaster's post. There is also a macro included that allows easy use. All this works without a heap allocator and is compatible with no_std.
use arrform::{arrform, ArrForm};
let af = arrform!(64, "write some stuff {}: {:.2}", "foo", 42.3456);
assert_eq!("write some stuff foo: 42.35", af.as_str());
This macro first reserves a buffer on the stack. Then it uses the struct ArrForm to format text and numbers. It returns an instance of ArrForm that allows easy access to the contained text. The macro panics if the buffer is chosen too small.
see https://github.com/Simsys/arrform or https://crates.io/crates/arrform.

Cannot use downcast Any to an array containing references

Passing just an array seems to work fine:
fn set_zero<'a>(u: &'a u32, ms: &mut [Option<&'a u32>; 4]) {
ms[0] = Some(u);
}
I'm trying to wrap C callback function which is passed a void *
so I'm using std::any::Any instead of an array:
fn set_zero<'a>(u: &'a u32, ma: &mut Any) {
if let Some(ms) = ma.downcast_mut::<[Option<&'a u32>; 4]>() {
ms[0] = Some(u);
}
}
This causes an error related to lifetimes:
error[E0477]: the type `[std::option::Option<&'a u32>; 4]`
does not fulfill the required lifetime
--> option.rs:18:26
|
18 | if let Some(ms) = ma.downcast_mut::<[Option<&'a u32>; 4]>() {
| ^^^^^^^^^^^^
|
= note: type must outlive the static lifetime
How can I fix this? What documentation should read to understand the error better?
UPDATE: more detailed background:
An contrived C prototype is - foo.c:
#include <stdint.h>
typedef int (*cb_t)(const uint32_t *obj, void *arg);
int32_t cb_run(uint32_t const objs[], uint32_t len, cb_t cb, void *arg)
{
int i;
for (i = 0; i < len; i++)
if (cb(&objs[i], arg) < 0)
return -1;
return 0;
}
Then, I tried to wrap by using Any:
extern crate libc;
use libc::{c_int, size_t, c_void};
use std::any::Any;
type CbT = extern "C" fn(obj: *const u32, arg: *mut c_void) -> c_int;
extern { fn cb_run(objs: *const u32, len: size_t, cb: CbT, arg: *mut c_void) -> c_int; }
type Cb = fn(obj: &u32, arg: &mut Any) -> i32;
struct CbData <'a> {
cb: Cb,
arg: &'a mut Any,
}
extern fn rust_cb(obj: *const u32, arg: *mut c_void) -> c_int {
unsafe {
let s = &mut *(arg as *mut CbData);
(s.cb)(&*obj, s.arg)
}
}
fn cb_run_rs(objs: &[u32], cb: Cb, arg: &mut Any) -> i32 {
let mut arg = &mut CbData{ cb: cb, arg: arg } ;
unsafe { cb_run(objs.as_ptr() as *const u32, objs.len() as size_t,
rust_cb, arg as *mut _ as *mut c_void) as i32 }
}
//// the above is lib, the below is bin
// set_zero() in the above
fn my_cb<'a>(obj: &'a u32, arg: &mut Any) -> i32 {
if let Some(data) = arg.downcast_mut::<[Option<&'a u32>; 4]>() {
data[0] = Some(obj);
}
0
}
fn main() {
let objs = [0u32, 1, 2, 3];
let mut arg = [None; 4];
println!("pre : {:?}", arg);
cb_run_rs(&objs, my_cb, &mut arg);
println!("post : {:?}", arg);
}
And it causes note: type must outlive the static lifetime.
How can I handle this kind of void * in good manner without unsafe from library user?
I can only tell it seems to work. main.rs
extern crate libc;
use libc::{c_int, size_t, c_void};
type CbT = extern "C" fn(obj: *const u32, arg: *mut c_void) -> c_int;
extern { fn cb_run(objs: *const u32, len: size_t, cb: CbT, arg: *mut c_void) -> c_int; }
type Cb <'a, T: ?Sized> = fn(obj: &'a u32, arg: &mut T) -> i32;
struct CbData <'a, 'b, T: 'a + 'b + ?Sized> {
cb: Cb<'a, T>,
arg: &'b mut T,
}
extern fn rust_cb<T: ?Sized>(obj: *const u32, arg: *mut c_void) -> c_int {
unsafe {
let s = &mut *(arg as *mut CbData<T>);
(s.cb)(&*obj, s.arg)
}
}
fn cb_run_rs<'a, 'b, T: 'a + ?Sized>(objs: &[u32], cb: Cb<'a, T>, arg: &'b mut T) -> i32 {
let mut arg = &mut CbData{ cb: cb, arg: arg } ;
unsafe { cb_run(objs.as_ptr() as *const u32, objs.len() as size_t,
rust_cb::<T>, arg as *mut _ as *mut c_void) as i32 }
}
//// the above is lib, the below is bin
fn my_cb<'a>(obj: &'a u32, arg: &mut [Option<&'a u32>]) -> i32 {
arg[*obj as usize] = Some(obj);
0
}
fn main() {
let objs = [0u32, 1, 2, 3];
let mut arg = [None; 4];
println!("pre : {:?}", arg);
cb_run_rs(&objs, my_cb, &mut arg);
println!("post : {:?}", arg);
}
FYI build.rs:
extern crate gcc;
fn main() {
gcc::compile_library("libfoo.a", &["src/foo.c"]);
}
Cargo.toml:
[package]
name = "sof"
version = "0.1.0"
authors = ["author#example.com"]
build = "build.rs"
[build-dependencies]
gcc = "0.3"
[dependencies]
libc = "0.2"

How to format output to a byte array with no_std and no allocator?

I want to do something like:
let x = 123;
let mut buf = [0 as u8; 20];
format_to!(x --> buf);
assert_eq!(&buf[..3], &b"123"[..]);
With #![no_std] and without any memory allocator.
As I understand, there is an implementation of core::fmt::Display for u64, and I want to use it if possible.
In other words, I want to do something like format!(...), but without a memory allocator. How can I do this?
Let's start with the standard version:
use std::io::Write;
fn main() {
let x = 123;
let mut buf = [0 as u8; 20];
write!(&mut buf[..], "{}", x).expect("Can't write");
assert_eq!(&buf[0..3], b"123");
}
If we then remove the standard library:
#![feature(lang_items)]
#![no_std]
use core::panic::PanicInfo;
#[lang = "eh_personality"]
extern "C" fn eh_personality() {}
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
loop {}
}
fn main() {
let x = 123;
let mut buf = [0 as u8; 20];
write!(&mut buf[..], "{}", x).expect("Can't write");
assert_eq!(&buf[0..3], b"123");
}
We get the error
error[E0599]: no method named `write_fmt` found for type `&mut [u8]` in the current scope
--> src/main.rs:17:5
|
17 | write!(&mut buf[..], "{}", x).expect("Can't write");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
write_fmt is implemented in the core library by core::fmt::Write. If we implement it ourselves, we are able to pass that error:
#![feature(lang_items)]
#![feature(start)]
#![no_std]
use core::panic::PanicInfo;
#[lang = "eh_personality"]
extern "C" fn eh_personality() {}
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
loop {}
}
use core::fmt::{self, Write};
struct Wrapper<'a> {
buf: &'a mut [u8],
offset: usize,
}
impl<'a> Wrapper<'a> {
fn new(buf: &'a mut [u8]) -> Self {
Wrapper {
buf: buf,
offset: 0,
}
}
}
impl<'a> fmt::Write for Wrapper<'a> {
fn write_str(&mut self, s: &str) -> fmt::Result {
let bytes = s.as_bytes();
// Skip over already-copied data
let remainder = &mut self.buf[self.offset..];
// Check if there is space remaining (return error instead of panicking)
if remainder.len() < bytes.len() { return Err(core::fmt::Error); }
// Make the two slices the same length
let remainder = &mut remainder[..bytes.len()];
// Copy
remainder.copy_from_slice(bytes);
// Update offset to avoid overwriting
self.offset += bytes.len();
Ok(())
}
}
#[start]
fn start(_argc: isize, _argv: *const *const u8) -> isize {
let x = 123;
let mut buf = [0 as u8; 20];
write!(Wrapper::new(&mut buf), "{}", x).expect("Can't write");
assert_eq!(&buf[0..3], b"123");
0
}
Note that we are duplicating the behavior of io::Cursor into this wrapper. Normally, multiple writes to a &mut [u8] will overwrite each other. This is good for reusing allocation, but not useful when you have consecutive writes of the same data.
Then it's just a matter of writing a macro if you want to.
You should also be able to use a crate like arrayvec, which has written this code for you. This is untested:
#![feature(lang_items)]
#![feature(start)]
#![no_std]
use core::panic::PanicInfo;
#[lang = "eh_personality"]
extern "C" fn eh_personality() {}
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
loop {}
}
use arrayvec::ArrayString; // 0.4.10
use core::fmt::Write;
#[start]
fn start(_argc: isize, _argv: *const *const u8) -> isize {
let x = 123;
let mut buf = ArrayString::<[u8; 20]>::new();
write!(&mut buf, "{}", x).expect("Can't write");
assert_eq!(&buf, "123");
0
}
With bare_io:
use bare_io::{Cursor, Write};
let mut buf = [0 as u8; 256];
let mut cur = Cursor::new(&mut buf[..]);
write!(&mut cur, "hello world, stack buf, {}\n\0", 234).expect("!write");
unsafe { puts(buf.as_ptr()) };
With bare_io, smallvec and alloc:
use smallvec::{Array, SmallVec};
struct WriteSmallVec<A: Array<Item = u8>>(SmallVec<A>);
impl<A: Array<Item = u8>> Write for WriteSmallVec<A> {
fn write(&mut self, buf: &[u8]) -> bare_io::Result<usize> {
self.0.extend_from_slice(buf);
Ok(buf.len())
}
fn flush(&mut self) -> bare_io::Result<()> {
Ok(())
}
}
let mut sv = WriteSmallVec(SmallVec::<[u8; 256]>::new());
write!(&mut sv, "hello world, SmallVec, prev len: {}\n\0", len).expect("!write");
unsafe { puts(sv.0.as_ptr()) };
With bare_io, patched inlinable_string and alloc:
use core::fmt::Write;
use inlinable_string::{InlinableString, StringExt};
let mut is = InlinableString::new();
write!(&mut is, "hello world, InlinableString, {}\n\0", 345).expect("!write");
unsafe { puts(is.as_ptr()) };
Tested in Linux kernel,
cargo build --release -Z build-std=core,alloc --target=x86_64-linux-kernel
Also did some benchmarks, comparing a simple array with SmallVec and InlinableString: https://gitlab.com/artemciy/lin-socks/-/blob/95d2bb96/bench/stack-string.rs
p.s. bare-io has been yanked though.

How to slice a large Vec<i32> as &[u8]?

I don't know how to convert a Vec<i32> into a &[u8] slice.
fn main() {
let v: Vec<i32> = vec![1; 100_000_000];
let v_bytes: &[u8] = /* ... */;
}
I want to write a large Vec<i32> to a file so I can read it back at a future time.
You can use std::slice::from_raw_parts:
let v_bytes: &[u8] = unsafe {
std::slice::from_raw_parts(
v.as_ptr() as *const u8,
v.len() * std::mem::size_of::<i32>(),
)
};
Following the comments on this answer, you should wrap this code in a function and have the return value borrow the input, so that you use the borrow checker as far as possible:
fn as_u8_slice(v: &[i32]) -> &[u8] {
unsafe {
std::slice::from_raw_parts(
v.as_ptr() as *const u8,
v.len() * std::mem::size_of::<i32>(),
)
}
}
Since Rust 1.30, the best solution is to use slice::align_to:
fn main() {
let v: Vec<i32> = vec![1; 8];
let (head, body, tail) = unsafe { v.align_to::<u8>() };
assert!(head.is_empty());
assert!(tail.is_empty());
println!("{:#x?}", body);
}
This properly handles the cases where the alignment of the first type and the second type do not match. In this example, I ensure that the alignment of the i32 is greater than that of the u8 via the assert! statements.
I took #swizards answer and ran with it a bit to get the other side of the coin - reading the vector back in:
use std::fs::File;
use std::io::{Read, Write};
use std::{mem, slice};
fn as_u8_slice(v: &[i32]) -> &[u8] {
let element_size = mem::size_of::<i32>();
unsafe { slice::from_raw_parts(v.as_ptr() as *const u8, v.len() * element_size) }
}
fn from_u8(v: Vec<u8>) -> Vec<i32> {
let data = v.as_ptr();
let len = v.len();
let capacity = v.capacity();
let element_size = mem::size_of::<i32>();
// Make sure we have a proper amount of capacity (may be overkill)
assert_eq!(capacity % element_size, 0);
// Make sure we are going to read a full chunk of stuff
assert_eq!(len % element_size, 0);
unsafe {
// Don't allow the current vector to be dropped
// (which would invalidate the memory)
mem::forget(v);
Vec::from_raw_parts(
data as *mut i32,
len / element_size,
capacity / element_size,
)
}
}
fn do_write(filename: &str, v: &[i32]) {
let mut f = File::create(filename).unwrap();
f.write_all(as_u8_slice(v)).unwrap();
}
fn do_read(filename: &str) -> Vec<i32> {
let mut f = File::open(filename).unwrap();
let mut bytes = Vec::new();
f.read_to_end(&mut bytes).unwrap();
from_u8(bytes)
}
fn main() {
let v = vec![42; 10];
do_write("vector.dump", &v);
let v2 = do_read("vector.dump");
assert_eq!(v, v2);
println!("{:?}", v2)
}

Resources