mirror of
https://forge.sourceware.org/marek/gcc.git
synced 2026-02-22 03:47:02 -05:00
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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ();
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user