gccrs: Improve libformat_parser FFI

This should remove a use-after-free as well as simplify the FFI
interface.

gcc/rust/ChangeLog:

	* ast/rust-fmt.cc (Pieces::collect): Handle changes to ffi
	interface.
	(Pieces::~Pieces): Remove function definition.
	(Pieces::Pieces): Likewise.
	(Pieces::operator=): Likewise.
	* ast/rust-fmt.h: Include "optional.h".
	(rust_ffi_alloc): New extern "C" function declaration.
	(rust_ffi_dealloc): Likewise.
	(class FFIVec): New class.
	(class FFIOpt): Likewise.
	(RustHamster::RustHamster): New constructor accepting const
	std::string reference.
	(struct FormatSpec): Use FFIOpt.
	(struct PieceSlice): Remove struct.
	(struct RustString): Likewise.
	(struct FormatArgsHandle): Likewise.
	(collect_pieces): Change function signature.
	(clone_pieces): Likewise.
	(destroy_pieces): Remove extern "C" function declaration.
	(Pieces::~Pieces): Remove function declaration.
	(Pieces::operator=): Likewise.
	(Pieces::get_pieces): Handle changes to class fields.
	(Pieces::Pieces): Remove copy and move constructor declarations,
	adjust signature of remaining constructor declaration.
	(Pieces::pieces_vector): Remove member variable.
	(Pieces::handle): Likewise.
	(Pieces::data): Add member variable.
	* expand/rust-macro-builtins-asm.cc (expand_inline_asm_strings):
	Use references to avoid copying.

libgrust/ChangeLog:

	* libformat_parser/src/lib.rs (struct FFIVec): New.
	(trait StringLeakExt): Remove.
	(struct FFIOpt): New.
	(trait IntoFFI): Adjust implementation for Option.
	(struct RustHamster): Add lifetime and adjust conversion to and
	from &str.
	(enum Piece): Adjust definition to handle changes to
	RustHamster.
	(struct Argument): Likewise.
	(struct FormatSpec): Use FFIOpt and RustHamster.
	(enum Position): Use RustHamster.
	(enum Count): Likewise.
	(struct PieceSlice): Replace with...
	(typedef PieceVec): ...this.
	(struct RustString): Remove.
	(struct FormatArgsHandle): Likewise.
	(fn collect_pieces): Adjust signature, greatly simplifying
	implementation.
	(fn clone_pieces): Likewise.
	(fn destroy_pieces): Remove.
	(trait LayoutExt): New.
	(fn rust_ffi_alloc): New.
	(fn rust_ffi_dealloc): New.

Signed-off-by: Owen Avery <powerboat9.gamer@gmail.com>
This commit is contained in:
Owen Avery
2025-08-12 20:02:55 -04:00
committed by Arthur Cohen
parent b76bb3f226
commit c8461400fd
4 changed files with 380 additions and 220 deletions

View File

@@ -32,41 +32,11 @@ Pieces
Pieces::collect (const std::string &to_parse, bool append_newline,
ffi::ParseMode parse_mode)
{
auto handle
= ffi::collect_pieces (to_parse.c_str (), append_newline, parse_mode);
// this performs multiple copies, can we avoid them maybe?
// TODO: Instead of just creating a vec of, basically, `ffi::Piece`s, we
// should transform them into the proper C++ type which we can work with. so
// transform all the strings into C++ strings? all the Option<T> into
// tl::optional<T>?
auto pieces_vector = std::vector<ffi::Piece> (handle.piece_slice.base_ptr,
handle.piece_slice.base_ptr
+ handle.piece_slice.len);
return Pieces (handle, std::move (pieces_vector));
Pieces ret (to_parse, ffi::FFIVec<ffi::Piece> ());
ret.data->second = ffi::collect_pieces (ffi::RustHamster (ret.data->first),
append_newline, parse_mode);
return ret;
}
Pieces::~Pieces () { ffi::destroy_pieces (handle); }
Pieces::Pieces (const Pieces &other) : pieces_vector (other.pieces_vector)
{
handle = ffi::clone_pieces (other.handle);
}
Pieces &
Pieces::operator= (const Pieces &other)
{
handle = ffi::clone_pieces (other.handle);
pieces_vector = other.pieces_vector;
return *this;
}
Pieces::Pieces (Pieces &&other)
: pieces_vector (std::move (other.pieces_vector)),
handle (clone_pieces (other.handle))
{}
} // namespace Fmt
} // namespace Rust

View File

@@ -20,20 +20,138 @@
#define RUST_FMT_H
#include "rust-system.h"
// FIXME: How to encode Option?
#include "optional.h"
namespace Rust {
namespace Fmt {
namespace ffi {
extern "C" {
unsigned char *rust_ffi_alloc (size_t count, size_t elem_size, size_t align);
void rust_ffi_dealloc (unsigned char *data, size_t count, size_t elem_size,
size_t align);
} // extern "C"
template <typename T> class FFIVec
{
T *data;
size_t len;
size_t cap;
public:
FFIVec () : data ((T *) alignof (T)), len (0), cap (0) {}
FFIVec (const FFIVec &) = delete;
FFIVec &operator= (const FFIVec &) = delete;
FFIVec (FFIVec &&other) : data (other.data), len (other.len), cap (other.cap)
{
other.data = (T *) alignof (T);
other.len = 0;
other.cap = 0;
}
FFIVec &operator= (FFIVec &&other)
{
this->~FFIVec ();
new (this) FFIVec (std::move (other));
return *this;
}
~FFIVec ()
{
// T can't be zero-sized
if (cap)
rust_ffi_dealloc ((unsigned char *) data, cap, sizeof (T), alignof (T));
}
size_t size () const { return len; }
const T &operator[] (size_t idx) const
{
rust_assert (idx <= len);
return data[idx];
}
T *begin () { return data; }
const T *begin () const { return data; }
T *end () { return data + len; }
const T *end () const { return data + len; }
};
template <typename T> class FFIOpt
{
struct alignas (T) Inner
{
char data[sizeof (T)];
} inner;
bool is_some;
public:
template <typename U> FFIOpt (U &&val) : is_some (true)
{
new (inner.data) T (std::forward<U> (val));
}
FFIOpt () : is_some (false) {}
FFIOpt (const FFIOpt &other) : is_some (other.is_some)
{
if (is_some)
new (inner.data) T (*(const T *) other.inner.data);
}
FFIOpt (FFIOpt &&other) : is_some (other.is_some)
{
if (is_some)
new (inner.data) T (std::move (*(const T *) other.inner.data));
}
~FFIOpt ()
{
if (is_some)
((T *) inner.data)->~T ();
}
FFIOpt &operator= (const FFIOpt &other)
{
this->~FFIOpt ();
new (this) FFIOpt (other);
return *this;
}
FFIOpt &operator= (FFIOpt &&other)
{
this->~FFIOpt ();
new (this) FFIOpt (std::move (other));
return *this;
}
tl::optional<std::reference_wrapper<T>> get_opt ()
{
return (T *) inner.data;
}
tl::optional<std::reference_wrapper<const T>> get_opt () const
{
return (const T *) inner.data;
}
};
struct RustHamster
{
const char *ptr;
size_t len;
std::string to_string () const;
explicit RustHamster (const std::string &str)
: ptr (str.data ()), len (str.size ())
{}
};
/// Enum of alignments which are supported.
@@ -166,33 +284,33 @@ struct Count
struct FormatSpec
{
/// Optionally specified character to fill alignment with.
const uint32_t *fill;
FFIOpt<uint32_t> fill;
/// Span of the optionally specified fill character.
const InnerSpan *fill_span;
FFIOpt<InnerSpan> fill_span;
/// Optionally specified alignment.
Alignment align;
/// The `+` or `-` flag.
const Sign *sign;
FFIOpt<Sign> sign;
/// The `#` flag.
bool alternate;
/// The `0` flag.
bool zero_pad;
/// The `x` or `X` flag. (Only for `Debug`.)
const DebugHex *debug_hex;
FFIOpt<DebugHex> debug_hex;
/// The integer precision to use.
Count precision;
/// The span of the precision formatting flag (for diagnostics).
const InnerSpan *precision_span;
FFIOpt<InnerSpan> precision_span;
/// The string width requested for the resulting format.
Count width;
/// The span of the width formatting flag (for diagnostics).
const InnerSpan *width_span;
FFIOpt<InnerSpan> width_span;
/// The descriptor string representing the name of the format desired for
/// this argument, this can be empty or any number of characters, although
/// it is required to be one word.
RustHamster ty;
/// The span of the descriptor string (for diagnostics).
const InnerSpan *ty_span;
FFIOpt<InnerSpan> ty_span;
};
/// Representation of an argument specification.
@@ -238,26 +356,6 @@ struct Piece
};
};
struct PieceSlice
{
const Piece *base_ptr;
size_t len;
size_t cap;
};
struct RustString
{
const unsigned char *ptr;
size_t len;
size_t cap;
};
struct FormatArgsHandle
{
PieceSlice piece_slice;
RustString rust_string;
};
enum ParseMode
{
Format = 0,
@@ -266,12 +364,10 @@ enum ParseMode
extern "C" {
FormatArgsHandle collect_pieces (const char *input, bool append_newline,
ParseMode parse_mode);
FFIVec<Piece> collect_pieces (RustHamster input, bool append_newline,
ParseMode parse_mode);
FormatArgsHandle clone_pieces (const FormatArgsHandle &);
void destroy_pieces (FormatArgsHandle);
FFIVec<Piece> clone_pieces (const FFIVec<Piece> &);
} // extern "C"
@@ -281,33 +377,20 @@ struct Pieces
{
static Pieces collect (const std::string &to_parse, bool append_newline,
ffi::ParseMode parse_mode);
~Pieces ();
Pieces (const Pieces &other);
Pieces &operator= (const Pieces &other);
Pieces (Pieces &&other);
const std::vector<ffi::Piece> &get_pieces () const { return pieces_vector; }
// {
// slice = clone_pieces (&other.slice);
// to_parse = other.to_parse;
// return *this;
// }
const ffi::FFIVec<ffi::Piece> &get_pieces () const { return data->second; }
private:
Pieces (ffi::FormatArgsHandle handle, std::vector<ffi::Piece> &&pieces_vector)
: pieces_vector (std::move (pieces_vector)), handle (handle)
Pieces (std::string str, ffi::FFIVec<ffi::Piece> pieces)
: data (
std::make_shared<decltype (data)::element_type> (std::move (str),
std::move (pieces)))
{}
std::vector<ffi::Piece> pieces_vector;
// this memory is held for FFI reasons - it needs to be released and cloned
// precisely, so try to not access it/modify it if possible. you should
// instead work with `pieces_vector`
ffi::FormatArgsHandle handle;
// makes copying simpler
// also, we'd need to keep the parsed string in a shared_ptr anyways
// since we store pointers into the parsed string
std::shared_ptr<std::pair<std::string, ffi::FFIVec<ffi::Piece>>> data;
};
} // namespace Fmt

View File

@@ -787,12 +787,12 @@ expand_inline_asm_strings (InlineAsmContext inline_asm_ctx)
auto pieces = Fmt::Pieces::collect (template_str.symbol, false,
Fmt::ffi::ParseMode::InlineAsm);
auto pieces_vec = pieces.get_pieces ();
auto &pieces_vec = pieces.get_pieces ();
std::string transformed_template_str = "";
for (size_t i = 0; i < pieces_vec.size (); i++)
{
auto piece = pieces_vec[i];
auto &piece = pieces_vec[i];
if (piece.tag == Fmt::ffi::Piece::Tag::String)
{
transformed_template_str += piece.string._0.to_string ();

View File

@@ -1,56 +1,197 @@
//! FFI interface for `rustc_format_parser`
use std::alloc::Layout;
// what's the plan? Have a function return something that can be constructed into a vector?
// or an iterator?
use std::ffi::CStr;
trait IntoFFI<T> {
fn into_ffi(self) -> T;
}
impl<T> IntoFFI<*const T> for Option<T>
where
T: Sized,
{
fn into_ffi(self) -> *const T {
match self.as_ref() {
None => std::ptr::null(),
Some(r) => r as *const T,
}
}
}
// Extension trait to provide `String::leak` which did not exist in Rust 1.49
pub trait StringLeakExt {
fn leak<'a>(self) -> &'a mut str;
}
impl StringLeakExt for String {
fn leak<'a>(self) -> &'a mut str {
Box::leak(self.into_boxed_str())
}
}
// FIXME: Make an ffi module in a separate file
// FIXME: Remember to leak the boxed type somehow
// FIXME: How to encode the Option type? As a pointer? Option<T> -> Option<&T> -> *const T could work maybe?
pub mod ffi {
use super::IntoFFI;
use std::marker::PhantomData;
use std::mem::MaybeUninit;
#[repr(C)]
pub struct FFIVec<T> {
data: *mut T,
len: usize,
cap: usize
}
impl<T> IntoFFI<FFIVec<T>> for Vec<T> {
fn into_ffi(mut self) -> FFIVec<T> {
let ret = FFIVec {
data: self.as_mut_ptr(),
len: self.len(),
cap: self.capacity()
};
self.leak();
ret
}
}
impl<T> Drop for FFIVec<T> {
fn drop(&mut self) {
unsafe {
Vec::from_raw_parts(self.data, self.len, self.cap);
}
}
}
impl<T> FFIVec<T> {
fn with_vec_ref<R, F: for<'a> FnOnce(&'a Vec<T>) -> R>(
&self, f: F
) -> R {
let v = unsafe {
Vec::from_raw_parts(self.data, self.len, self.cap)
};
let ret = f(&v);
v.leak();
ret
}
// currently unused
// may be nice to have later, though
#[allow(unused)]
fn with_vec_mut_ref<R, F: for<'a> FnOnce(&'a mut Vec<T>) -> R>(
&mut self, f: F
) -> R {
let mut v = unsafe {
Vec::from_raw_parts(self.data, self.len, self.cap)
};
let ret = f(&mut v);
self.data = v.as_mut_ptr();
self.len = v.len();
self.cap = v.capacity();
v.leak();
ret
}
}
impl<T> Clone for FFIVec<T>
where
T: Clone
{
fn clone(&self) -> FFIVec<T> {
self.with_vec_ref(|v| v.clone().into_ffi())
}
}
#[repr(C)]
pub struct FFIOpt<T> {
val: MaybeUninit<T>,
is_some: bool
}
impl<T> Clone for FFIOpt<T>
where
T: Clone
{
fn clone(&self) -> Self {
match self.get_opt_ref() {
Some(r) => FFIOpt::new_val(r.clone()),
None => FFIOpt::new_none()
}
}
}
impl<T> PartialEq for FFIOpt<T>
where
T: PartialEq
{
fn eq(&self, other: &Self) -> bool {
match (self.get_opt_ref(), other.get_opt_ref()) {
(Some(a), Some(b)) => a.eq(b),
_ => false
}
}
}
impl<T> Eq for FFIOpt<T>
where
T: Eq
{}
impl<T> Drop for FFIOpt<T>
{
fn drop(&mut self) {
if self.is_some {
unsafe { std::ptr::drop_in_place(self.val.as_mut_ptr()) }
}
}
}
impl<T> IntoFFI<FFIOpt<T>> for Option<T> {
fn into_ffi(self) -> FFIOpt<T> {
match self {
Some(v) => FFIOpt::new_val(v),
None => FFIOpt::new_none()
}
}
}
impl<T> FFIOpt<T> {
pub fn new_val(v: T) -> Self {
FFIOpt {
val: MaybeUninit::new(v),
is_some: true
}
}
pub fn new_none() -> Self {
FFIOpt {
val: MaybeUninit::uninit(),
is_some: false
}
}
pub fn get_opt_ref(&self) -> Option<&T> {
if self.is_some {
Some(unsafe {&*self.val.as_ptr()})
} else {
None
}
}
pub fn get_opt_ref_mut(&mut self) -> Option<&mut T> {
if self.is_some {
Some(unsafe {&mut *self.val.as_mut_ptr()})
} else {
None
}
}
}
// FIXME: We need to ensure we deal with memory properly - whether it's owned by the C++ side or the Rust side
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[repr(C)]
pub struct RustHamster {
pub struct RustHamster<'a> {
ptr: *const u8,
len: usize,
phantom: PhantomData<&'a u8>
}
impl<'a> From<&'a str> for RustHamster {
fn from(s: &'a str) -> RustHamster {
impl<'a> IntoFFI<RustHamster<'a>> for &'a str {
fn into_ffi(self) -> RustHamster<'a> {
RustHamster {
ptr: s.as_ptr(),
len: s.len(),
ptr: self.as_ptr(),
len: self.len(),
phantom: PhantomData,
}
}
}
impl<'a> RustHamster<'a> {
pub fn as_str(&self) -> &'a str {
unsafe {
let slice: &'a [u8] = std::slice::from_raw_parts(self.ptr, self.len);
std::str::from_utf8_unchecked(slice)
}
}
}
@@ -101,11 +242,11 @@ pub mod ffi {
/// A piece is a portion of the format string which represents the next part
/// to emit. These are emitted as a stream by the `Parser` class.
#[derive(Debug, Clone, PartialEq)]
#[derive(Clone)]
#[repr(C)]
pub enum Piece<'a> {
/// A literal string which should directly be emitted
String(RustHamster),
String(RustHamster<'a>),
/// This describes that formatting should process the next argument (as
/// specified inside) for emission.
// do we need a pointer here? we're doing big cloning anyway
@@ -113,7 +254,7 @@ pub mod ffi {
}
/// Representation of an argument specification.
#[derive(Copy, Clone, Debug, PartialEq)]
#[derive(Clone)]
#[repr(C)]
pub struct Argument<'a> {
/// Where to find this argument
@@ -126,37 +267,37 @@ pub mod ffi {
}
/// Specification for the formatting of an argument in the format string.
#[derive(Copy, Clone, Debug, PartialEq)]
#[derive(Clone)]
#[repr(C)]
pub struct FormatSpec<'a> {
/// Optionally specified character to fill alignment with.
pub fill: Option<char>,
pub fill: FFIOpt<char>,
/// Span of the optionally specified fill character.
pub fill_span: *const InnerSpan,
pub fill_span: FFIOpt<InnerSpan>,
/// Optionally specified alignment.
pub align: Alignment,
/// The `+` or `-` flag.
pub sign: *const Sign,
pub sign: FFIOpt<Sign>,
/// The `#` flag.
pub alternate: bool,
/// The `0` flag.
pub zero_pad: bool,
/// The `x` or `X` flag. (Only for `Debug`.)
pub debug_hex: *const DebugHex,
pub debug_hex: FFIOpt<DebugHex>,
/// The integer precision to use.
pub precision: Count<'a>,
/// The span of the precision formatting flag (for diagnostics).
pub precision_span: *const InnerSpan,
pub precision_span: FFIOpt<InnerSpan>,
/// The string width requested for the resulting format.
pub width: Count<'a>,
/// The span of the width formatting flag (for diagnostics).
pub width_span: *const InnerSpan,
pub width_span: FFIOpt<InnerSpan>,
/// The descriptor string representing the name of the format desired for
/// this argument, this can be empty or any number of characters, although
/// it is required to be one word.
pub ty: &'a str,
pub ty: RustHamster<'a>,
/// The span of the descriptor string (for diagnostics).
pub ty_span: *const InnerSpan,
pub ty_span: FFIOpt<InnerSpan>,
}
/// Enum describing where an argument for a format can be located.
@@ -168,7 +309,7 @@ pub mod ffi {
/// The argument is located at a specific index given in the format,
ArgumentIs(usize),
/// The argument has a name.
ArgumentNamed(&'a str),
ArgumentNamed(RustHamster<'a>),
}
/// Enum of alignments which are supported.
@@ -213,7 +354,7 @@ pub mod ffi {
/// The count is specified explicitly.
CountIs(usize),
/// The count is specified by the argument with the given name.
CountIsName(&'a str, InnerSpan),
CountIsName(RustHamster<'a>, InnerSpan),
/// The count is specified by the argument at the given index.
CountIsParam(usize),
/// The count is specified by a star (like in `{:.*}`) that refers to the argument at the given index.
@@ -225,7 +366,7 @@ pub mod ffi {
impl<'a> From<generic_format_parser::Piece<'a>> for Piece<'a> {
fn from(old: generic_format_parser::Piece<'a>) -> Self {
match old {
generic_format_parser::Piece::String(x) => Piece::String(x.into()),
generic_format_parser::Piece::String(x) => Piece::String(x.into_ffi()),
generic_format_parser::Piece::NextArgument(x) => {
// FIXME: This is problematic - if we do this, then we probably run into the issue that the Box
// is freed at the end of the call to collect_pieces. if we just .leak() it, then we have
@@ -259,7 +400,7 @@ pub mod ffi {
}
generic_format_parser::Position::ArgumentIs(x) => Position::ArgumentIs(x.into()),
generic_format_parser::Position::ArgumentNamed(x) => {
Position::ArgumentNamed(x.into())
Position::ArgumentNamed(x.into_ffi())
}
}
}
@@ -277,7 +418,7 @@ pub mod ffi {
impl<'a> From<generic_format_parser::FormatSpec<'a>> for FormatSpec<'a> {
fn from(old: generic_format_parser::FormatSpec<'a>) -> Self {
FormatSpec {
fill: old.fill,
fill: old.fill.into_ffi(),
fill_span: old.fill_span.map(Into::into).into_ffi(),
align: old.align.into(),
sign: old.sign.map(Into::into).into_ffi(),
@@ -288,7 +429,7 @@ pub mod ffi {
precision_span: old.precision_span.map(Into::into).into_ffi(),
width: old.width.into(),
width_span: old.width_span.map(Into::into).into_ffi(),
ty: old.ty,
ty: old.ty.into_ffi(),
ty_span: old.ty_span.map(Into::into).into_ffi(),
}
}
@@ -307,7 +448,7 @@ pub mod ffi {
fn from(old: generic_format_parser::Count<'a>) -> Self {
match old {
generic_format_parser::Count::CountIs(x) => Count::CountIs(x),
generic_format_parser::Count::CountIsName(x, y) => Count::CountIsName(x, y.into()),
generic_format_parser::Count::CountIsName(x, y) => Count::CountIsName(x.into_ffi(), y.into()),
generic_format_parser::Count::CountIsParam(x) => Count::CountIsParam(x),
generic_format_parser::Count::CountIsStar(x) => Count::CountIsStar(x),
generic_format_parser::Count::CountImplied => Count::CountImplied,
@@ -357,100 +498,66 @@ pub mod rust {
}
// TODO: Should we instead make an FFIVector struct?
#[repr(C)]
pub struct PieceSlice {
base_ptr: *mut ffi::Piece<'static /* FIXME: That's wrong */>,
len: usize,
cap: usize,
}
#[repr(C)]
// FIXME: we should probably use FFIString here
pub struct RustString {
ptr: *const u8,
len: usize,
cap: usize,
}
#[repr(C)]
pub struct FormatArgsHandle(PieceSlice, RustString);
type PieceVec<'a> = ffi::FFIVec<ffi::Piece<'a>>;
#[no_mangle]
pub extern "C" fn collect_pieces(
input: *const libc::c_char,
pub extern "C" fn collect_pieces<'a>(
input: ffi::RustHamster<'a>,
append_newline: bool,
parse_mode: crate::ffi::ParseMode,
) -> FormatArgsHandle {
// FIXME: Add comment
let str = unsafe { CStr::from_ptr(input) };
let str = str.to_str().unwrap().to_owned();
// we are never going to free this string here (we leak it later on), so we can extend its lifetime
// to send it across an FFI boundary.
// FIXME: Is that correct?
let s = &str;
let s = unsafe { std::mem::transmute::<&'_ str, &'static str>(s) };
) -> PieceVec<'a> {
// FIXME: No unwrap
let pieces: Vec<ffi::Piece<'_>> =
rust::collect_pieces(s, None, None, append_newline, parse_mode)
rust::collect_pieces(input.as_str(), None, None, append_newline, parse_mode)
.into_iter()
.map(Into::into)
.collect();
let piece_slice = PieceSlice {
len: pieces.len(),
cap: pieces.capacity(),
base_ptr: pieces.leak().as_mut_ptr(),
};
let rust_string = RustString {
len: str.len(),
cap: str.capacity(),
ptr: str.leak().as_ptr(),
};
FormatArgsHandle(piece_slice, rust_string)
pieces.into_ffi()
}
#[no_mangle]
pub unsafe extern "C" fn destroy_pieces(FormatArgsHandle(piece_slice, s): FormatArgsHandle) {
let PieceSlice { base_ptr, len, cap } = piece_slice;
drop(Vec::from_raw_parts(base_ptr, len, cap));
pub extern "C" fn clone_pieces<'a, 'b>(
piece_vec: &'a PieceVec<'b>
) -> PieceVec<'b> {
piece_vec.clone()
}
let RustString { ptr, len, cap } = s;
drop(String::from_raw_parts(ptr as *mut u8, len, cap));
// we need Layout::repeat
// function signature is a bit different, so call it repeat_x
trait LayoutExt {
fn repeat_x(&self, n: usize) -> Layout;
}
impl LayoutExt for Layout {
fn repeat_x(&self, n: usize) -> Layout {
let elem = self.pad_to_align();
let total_size = elem.size().checked_mul(n).unwrap();
Layout::from_size_align(total_size, elem.align()).unwrap()
}
}
#[no_mangle]
pub extern "C" fn clone_pieces(
FormatArgsHandle(piece_slice, s): &FormatArgsHandle,
) -> FormatArgsHandle {
let PieceSlice { base_ptr, len, cap } = *piece_slice;
let v = unsafe { Vec::from_raw_parts(base_ptr, len, cap) };
let cloned_v = v.clone();
// FIXME: Add documentation
v.leak();
let piece_slice = PieceSlice {
len: cloned_v.len(),
cap: cloned_v.capacity(),
base_ptr: cloned_v.leak().as_mut_ptr(),
};
let RustString { ptr, len, cap } = *s;
let s = unsafe { String::from_raw_parts(ptr as *mut u8, len, cap) };
let cloned_s = s.clone();
// FIXME: Documentation
s.leak();
let rust_string = RustString {
len: cloned_s.len(),
cap: cloned_s.capacity(),
ptr: cloned_s.leak().as_ptr(),
};
FormatArgsHandle(piece_slice, rust_string)
pub unsafe extern "C" fn rust_ffi_alloc(
count: usize, elem_size: usize, align: usize
) -> *mut u8 {
unsafe {
std::alloc::alloc(
Layout::from_size_align_unchecked(elem_size, align)
.repeat_x(count)
)
}
}
#[no_mangle]
pub unsafe extern "C" fn rust_ffi_dealloc(
data: *mut u8, count: usize, elem_size: usize, align: usize
) {
unsafe {
std::alloc::dealloc(
data,
Layout::from_size_align_unchecked(elem_size, align)
.repeat_x(count)
)
}
}