From cb3dadb4384730a9ee5ad9bee2dd4a6d6bdbf925 Mon Sep 17 00:00:00 2001 From: Arthur Cohen Date: Wed, 26 Mar 2025 14:42:04 +0000 Subject: [PATCH 1/2] ci: Use Rust 1.49 as the default toolchain Fixes gccrs#3569 ChangeLog: * .github/workflows/ccpp.yml: Switch to Rust 1.49. * .github/workflows/ccpp32alpine.yml: Likewise. --- .github/workflows/ccpp.yml | 12 ++++++------ .github/workflows/ccpp32alpine.yml | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ccpp.yml b/.github/workflows/ccpp.yml index a9484d65386..d996b3c372a 100644 --- a/.github/workflows/ccpp.yml +++ b/.github/workflows/ccpp.yml @@ -42,7 +42,7 @@ jobs: g++-multilib \ dejagnu; # install Rust directly using rustup - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain=1.72.0; + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain=1.49.0; - name: Make Source Read-Only run: chmod -R a-w ./* @@ -127,7 +127,7 @@ jobs: g++-multilib \ dejagnu; # install Rust directly using rustup - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain=1.72.0; + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain=1.49.0; - name: Make Source Read-Only run: chmod -R a-w ./* @@ -212,7 +212,7 @@ jobs: g++-multilib \ dejagnu; # install Rust directly using rustup - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain=1.72.0; + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain=1.49.0; - name: Make Source Read-Only run: chmod -R a-w ./* @@ -295,7 +295,7 @@ jobs: build-essential \ dejagnu; # install Rust directly using rustup - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain=1.72.0; + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain=1.49.0; - name: Restore cached gcc-5.4 id: restore-gcc5 @@ -393,7 +393,7 @@ jobs: run: | brew install dejagnu mpfr libmpc gmp; # install Rust directly using rustup - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain=1.72.0; + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain=1.49.0; - name: Make Source Read-Only run: chmod -R a-w ./* @@ -470,7 +470,7 @@ jobs: g++-multilib \ dejagnu; # install Rust directly using rustup - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain=1.72.0; + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain=1.49.0; - name: Make Source Read-Only run: chmod -R a-w ./* diff --git a/.github/workflows/ccpp32alpine.yml b/.github/workflows/ccpp32alpine.yml index 5eb0c230df2..24e24caf6de 100644 --- a/.github/workflows/ccpp32alpine.yml +++ b/.github/workflows/ccpp32alpine.yml @@ -50,7 +50,7 @@ jobs: # sudo apt install gcc-multilib g++-multilib libc6-dev-i386 # - name: Install Rust # run: | - # curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain=1.72.0 + # curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain=1.49.0 # shell: alpine.sh {0} - name: Install dependencies with gcc From 18c776c3da617523fb76f7ccc86c13a0c4422cdb Mon Sep 17 00:00:00 2001 From: Owen Avery Date: Tue, 12 Aug 2025 20:02:55 -0400 Subject: [PATCH 2/2] 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 --- gcc/rust/ast/rust-fmt.cc | 38 +-- gcc/rust/ast/rust-fmt.h | 195 +++++++---- gcc/rust/expand/rust-macro-builtins-asm.cc | 4 +- libgrust/libformat_parser/src/lib.rs | 359 +++++++++++++-------- 4 files changed, 378 insertions(+), 218 deletions(-) diff --git a/gcc/rust/ast/rust-fmt.cc b/gcc/rust/ast/rust-fmt.cc index a29c8203ae8..21f4f03bb6f 100644 --- a/gcc/rust/ast/rust-fmt.cc +++ b/gcc/rust/ast/rust-fmt.cc @@ -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 into - // tl::optional? - auto pieces_vector = std::vector (handle.piece_slice.base_ptr, - handle.piece_slice.base_ptr - + handle.piece_slice.len); - - return Pieces (handle, std::move (pieces_vector)); -} - -Pieces::~Pieces () { ffi::destroy_pieces (handle); } - -Pieces::Pieces (const Pieces &other) : pieces_vector (other.pieces_vector) -{ - handle = ffi::clone_pieces (other.handle); + Pieces ret (to_parse, ffi::FFIVec ()); + ret.data->second = ffi::collect_pieces (ffi::RustHamster (ret.data->first), + append_newline, parse_mode); + return ret; } -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 diff --git a/gcc/rust/ast/rust-fmt.h b/gcc/rust/ast/rust-fmt.h index 3722db291ab..c23499c3709 100644 --- a/gcc/rust/ast/rust-fmt.h +++ b/gcc/rust/ast/rust-fmt.h @@ -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 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 class FFIOpt +{ + struct alignas (T) Inner + { + char data[sizeof (T)]; + } inner; + bool is_some; + +public: + template FFIOpt (U &&val) : is_some (true) + { + new (inner.data) T (std::forward (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> get_opt () + { + return (T *) inner.data; + } + + tl::optional> 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 fill; /// Span of the optionally specified fill character. - const InnerSpan *fill_span; + FFIOpt fill_span; /// Optionally specified alignment. Alignment align; /// The `+` or `-` flag. - const Sign *sign; + FFIOpt sign; /// The `#` flag. bool alternate; /// The `0` flag. bool zero_pad; /// The `x` or `X` flag. (Only for `Debug`.) - const DebugHex *debug_hex; + FFIOpt debug_hex; /// The integer precision to use. Count precision; /// The span of the precision formatting flag (for diagnostics). - const InnerSpan *precision_span; + FFIOpt 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 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 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 collect_pieces (RustHamster input, bool append_newline, + ParseMode parse_mode); -FormatArgsHandle clone_pieces (const FormatArgsHandle &); - -void destroy_pieces (FormatArgsHandle); +FFIVec clone_pieces (const FFIVec &); } // 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 &get_pieces () const { return pieces_vector; } - - // { - // slice = clone_pieces (&other.slice); - // to_parse = other.to_parse; - - // return *this; - // } + const ffi::FFIVec &get_pieces () const { return data->second; } private: - Pieces (ffi::FormatArgsHandle handle, std::vector &&pieces_vector) - : pieces_vector (std::move (pieces_vector)), handle (handle) + Pieces (std::string str, ffi::FFIVec pieces) + : data ( + std::make_shared (std::move (str), + std::move (pieces))) {} - std::vector 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>> data; }; } // namespace Fmt diff --git a/gcc/rust/expand/rust-macro-builtins-asm.cc b/gcc/rust/expand/rust-macro-builtins-asm.cc index 6cbcf87ebc9..c991ca703e9 100644 --- a/gcc/rust/expand/rust-macro-builtins-asm.cc +++ b/gcc/rust/expand/rust-macro-builtins-asm.cc @@ -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 (); diff --git a/libgrust/libformat_parser/src/lib.rs b/libgrust/libformat_parser/src/lib.rs index 72e5971ae80..049403db0b7 100644 --- a/libgrust/libformat_parser/src/lib.rs +++ b/libgrust/libformat_parser/src/lib.rs @@ -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 { fn into_ffi(self) -> T; } -impl IntoFFI<*const T> for Option -where - T: Sized, -{ - fn into_ffi(self) -> *const T { - match self.as_ref() { - None => std::ptr::null(), - Some(r) => r as *const T, +// 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 -> 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 { + data: *mut T, + len: usize, + cap: usize + } + + impl IntoFFI> for Vec { + fn into_ffi(mut self) -> FFIVec { + let ret = FFIVec { + data: self.as_mut_ptr(), + len: self.len(), + cap: self.capacity() + }; + self.leak(); + ret } } -} -// 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 Drop for FFIVec { + fn drop(&mut self) { + unsafe { + Vec::from_raw_parts(self.data, self.len, self.cap); + } + } + } -impl StringLeakExt for String { - fn leak<'a>(self) -> &'a mut str { - Box::leak(self.into_boxed_str()) + impl FFIVec { + fn with_vec_ref FnOnce(&'a Vec) -> 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 FnOnce(&'a mut Vec) -> 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 + } } -} -// 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 -> Option<&T> -> *const T could work maybe? -pub mod ffi { - use super::IntoFFI; + impl Clone for FFIVec + where + T: Clone + { + fn clone(&self) -> FFIVec { + self.with_vec_ref(|v| v.clone().into_ffi()) + } + } + + #[repr(C)] + pub struct FFIOpt { + val: MaybeUninit, + is_some: bool + } + + impl Clone for FFIOpt + where + T: Clone + { + fn clone(&self) -> Self { + match self.get_opt_ref() { + Some(r) => FFIOpt::new_val(r.clone()), + None => FFIOpt::new_none() + } + } + } + + impl PartialEq for FFIOpt + 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 Eq for FFIOpt + where + T: Eq + {} + + impl Drop for FFIOpt + { + fn drop(&mut self) { + if self.is_some { + unsafe { std::ptr::drop_in_place(self.val.as_mut_ptr()) } + } + } + } + + impl IntoFFI> for Option { + fn into_ffi(self) -> FFIOpt { + match self { + Some(v) => FFIOpt::new_val(v), + None => FFIOpt::new_none() + } + } + } + + impl FFIOpt { + 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> 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, + pub fill: FFIOpt, /// Span of the optionally specified fill character. - pub fill_span: *const InnerSpan, + pub fill_span: FFIOpt, /// Optionally specified alignment. pub align: Alignment, /// The `+` or `-` flag. - pub sign: *const Sign, + pub sign: FFIOpt, /// 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, /// 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, /// 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, /// 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, } /// 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> 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> 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>; #[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> = - 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)); - - let RustString { ptr, len, cap } = s; - drop(String::from_raw_parts(ptr as *mut u8, len, cap)); +pub extern "C" fn clone_pieces<'a, 'b>( + piece_vec: &'a PieceVec<'b> +) -> PieceVec<'b> { + piece_vec.clone() } -#[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(); +// we need Layout::repeat +// function signature is a bit different, so call it repeat_x +trait LayoutExt { + fn repeat_x(&self, n: usize) -> Layout; +} - // FIXME: Documentation - s.leak(); +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() + } +} - let rust_string = RustString { - len: cloned_s.len(), - cap: cloned_s.capacity(), - ptr: cloned_s.leak().as_ptr(), - }; +#[no_mangle] +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) + ) + } +} - FormatArgsHandle(piece_slice, rust_string) +#[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) + ) + } }