| //! 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? | |
| trait IntoFFI<T> { | |
| fn into_ffi(self) -> 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<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()) | |
| } | |
| } | |
| // https://github.com/rust-lang/rfcs/blob/master/text/2195-really-tagged-unions.md | |
| #[repr(u8)] | |
| #[derive(Copy, Clone, PartialEq, Eq)] | |
| pub enum FFIOpt<T> { | |
| Some(T), | |
| None | |
| } | |
| impl<T> IntoFFI<FFIOpt<T>> for Option<T> { | |
| fn into_ffi(self) -> FFIOpt<T> { | |
| match self { | |
| Some(v) => FFIOpt::Some(v), | |
| None => FFIOpt::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<'a> { | |
| ptr: *const u8, | |
| len: usize, | |
| phantom: PhantomData<&'a u8> | |
| } | |
| impl<'a> IntoFFI<RustHamster<'a>> for &'a str { | |
| fn into_ffi(self) -> RustHamster<'a> { | |
| RustHamster { | |
| 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) | |
| } | |
| } | |
| } | |
| // Note: copied from rustc_span | |
| /// Range inside of a `Span` used for diagnostics when we only have access to relative positions. | |
| #[derive(Copy, Clone, PartialEq, Eq, Debug)] | |
| #[repr(C)] | |
| pub struct InnerSpan { | |
| pub start: usize, | |
| pub end: usize, | |
| } | |
| /// The location and before/after width of a character whose width has changed from its source code | |
| /// representation | |
| #[derive(Copy, Clone, PartialEq, Eq)] | |
| #[repr(C)] | |
| pub struct InnerWidthMapping { | |
| /// Index of the character in the source | |
| pub position: usize, | |
| /// The inner width in characters | |
| pub before: usize, | |
| /// The transformed width in characters | |
| pub after: usize, | |
| } | |
| // TODO: Not needed for now? | |
| // /// Whether the input string is a literal. If yes, it contains the inner width mappings. | |
| // #[derive(Clone, PartialEq, Eq)] | |
| // #[repr(C)] | |
| // enum InputStringKind { | |
| // NotALiteral, | |
| // Literal { | |
| // width_mappings: Vec<InnerWidthMapping>, | |
| // }, | |
| // } | |
| // TODO: Not needed for now? | |
| // /// The type of format string that we are parsing. | |
| #[derive(Copy, Clone, Debug, Eq, PartialEq)] | |
| #[repr(C)] | |
| pub enum ParseMode { | |
| /// A normal format string as per `format_args!`. | |
| Format = 0, | |
| /// An inline assembly template string for `asm!`. | |
| InlineAsm, | |
| } | |
| /// 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(Clone)] | |
| #[repr(C)] | |
| pub enum Piece<'a> { | |
| /// A literal string which should directly be emitted | |
| 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 | |
| NextArgument(Argument<'a>), | |
| } | |
| /// Representation of an argument specification. | |
| #[derive(Clone)] | |
| #[repr(C)] | |
| pub struct Argument<'a> { | |
| /// Where to find this argument | |
| pub position: Position<'a>, | |
| /// The span of the position indicator. Includes any whitespace in implicit | |
| /// positions (`{ }`). | |
| pub position_span: InnerSpan, | |
| /// How to format the argument | |
| pub format: FormatSpec<'a>, | |
| } | |
| /// Specification for the formatting of an argument in the format string. | |
| #[derive(Clone)] | |
| #[repr(C)] | |
| pub struct FormatSpec<'a> { | |
| /// Optionally specified character to fill alignment with. | |
| pub fill: FFIOpt<char>, | |
| /// Span of the optionally specified fill character. | |
| pub fill_span: FFIOpt<InnerSpan>, | |
| /// Optionally specified alignment. | |
| pub align: Alignment, | |
| /// The `+` or `-` flag. | |
| 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: FFIOpt<DebugHex>, | |
| /// The integer precision to use. | |
| pub precision: Count<'a>, | |
| /// The span of the precision formatting flag (for diagnostics). | |
| 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: 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: RustHamster<'a>, | |
| /// The span of the descriptor string (for diagnostics). | |
| pub ty_span: FFIOpt<InnerSpan>, | |
| } | |
| /// Enum describing where an argument for a format can be located. | |
| #[derive(Copy, Clone, Debug, PartialEq)] | |
| #[repr(C)] | |
| pub enum Position<'a> { | |
| /// The argument is implied to be located at an index | |
| ArgumentImplicitlyIs(usize), | |
| /// The argument is located at a specific index given in the format, | |
| ArgumentIs(usize), | |
| /// The argument has a name. | |
| ArgumentNamed(RustHamster<'a>), | |
| } | |
| /// Enum of alignments which are supported. | |
| #[derive(Copy, Clone, Debug, PartialEq)] | |
| #[repr(C)] | |
| pub enum Alignment { | |
| /// The value will be aligned to the left. | |
| AlignLeft, | |
| /// The value will be aligned to the right. | |
| AlignRight, | |
| /// The value will be aligned in the center. | |
| AlignCenter, | |
| /// The value will take on a default alignment. | |
| AlignUnknown, | |
| } | |
| /// Enum for the sign flags. | |
| #[derive(Copy, Clone, Debug, PartialEq)] | |
| #[repr(C)] | |
| pub enum Sign { | |
| /// The `+` flag. | |
| Plus, | |
| /// The `-` flag. | |
| Minus, | |
| } | |
| /// Enum for the debug hex flags. | |
| #[derive(Copy, Clone, Debug, PartialEq)] | |
| #[repr(C)] | |
| pub enum DebugHex { | |
| /// The `x` flag in `{:x?}`. | |
| Lower, | |
| /// The `X` flag in `{:X?}`. | |
| Upper, | |
| } | |
| /// A count is used for the precision and width parameters of an integer, and | |
| /// can reference either an argument or a literal integer. | |
| #[derive(Copy, Clone, Debug, PartialEq)] | |
| #[repr(C)] | |
| pub enum Count<'a> { | |
| /// The count is specified explicitly. | |
| CountIs(usize), | |
| /// The count is specified by the argument with the given name. | |
| 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. | |
| CountIsStar(usize), | |
| /// The count is implied and cannot be explicitly specified. | |
| CountImplied, | |
| } | |
| 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_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 | |
| // a memory leak... should we resend the info back to the Rust lib afterwards to free it? | |
| // this is definitely the best way - store that pointer in the FFI piece and rebuild the box | |
| // in a Rust destructor | |
| let ptr = Box::leak(x); | |
| let dst = Into::<Argument>::into(*ptr); | |
| Piece::NextArgument(dst) | |
| } | |
| } | |
| } | |
| } | |
| impl<'a> From<generic_format_parser::Argument<'a>> for Argument<'a> { | |
| fn from(old: generic_format_parser::Argument<'a>) -> Self { | |
| Argument { | |
| position: old.position.into(), | |
| position_span: old.position_span.into(), | |
| format: old.format.into(), | |
| } | |
| } | |
| } | |
| impl<'a> From<generic_format_parser::Position<'a>> for Position<'a> { | |
| fn from(old: generic_format_parser::Position<'a>) -> Self { | |
| match old { | |
| generic_format_parser::Position::ArgumentImplicitlyIs(x) => { | |
| Position::ArgumentImplicitlyIs(x.into()) | |
| } | |
| generic_format_parser::Position::ArgumentIs(x) => Position::ArgumentIs(x.into()), | |
| generic_format_parser::Position::ArgumentNamed(x) => { | |
| Position::ArgumentNamed(x.into_ffi()) | |
| } | |
| } | |
| } | |
| } | |
| impl From<generic_format_parser::InnerSpan> for InnerSpan { | |
| fn from(old: generic_format_parser::InnerSpan) -> Self { | |
| InnerSpan { | |
| start: old.start, | |
| end: old.end, | |
| } | |
| } | |
| } | |
| impl<'a> From<generic_format_parser::FormatSpec<'a>> for FormatSpec<'a> { | |
| fn from(old: generic_format_parser::FormatSpec<'a>) -> Self { | |
| FormatSpec { | |
| 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(), | |
| alternate: old.alternate, | |
| zero_pad: old.zero_pad, | |
| debug_hex: old.debug_hex.map(Into::into).into_ffi(), | |
| precision: old.precision.into(), | |
| 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.into_ffi(), | |
| ty_span: old.ty_span.map(Into::into).into_ffi(), | |
| } | |
| } | |
| } | |
| impl From<generic_format_parser::DebugHex> for DebugHex { | |
| fn from(old: generic_format_parser::DebugHex) -> Self { | |
| match old { | |
| generic_format_parser::DebugHex::Lower => DebugHex::Lower, | |
| generic_format_parser::DebugHex::Upper => DebugHex::Upper, | |
| } | |
| } | |
| } | |
| impl<'a> From<generic_format_parser::Count<'a>> for Count<'a> { | |
| 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.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, | |
| } | |
| } | |
| } | |
| impl From<generic_format_parser::Sign> for Sign { | |
| fn from(old: generic_format_parser::Sign) -> Self { | |
| match old { | |
| generic_format_parser::Sign::Plus => Sign::Plus, | |
| generic_format_parser::Sign::Minus => Sign::Minus, | |
| } | |
| } | |
| } | |
| impl From<generic_format_parser::Alignment> for Alignment { | |
| fn from(old: generic_format_parser::Alignment) -> Self { | |
| match old { | |
| generic_format_parser::Alignment::AlignLeft => Alignment::AlignLeft, | |
| generic_format_parser::Alignment::AlignRight => Alignment::AlignRight, | |
| generic_format_parser::Alignment::AlignCenter => Alignment::AlignCenter, | |
| generic_format_parser::Alignment::AlignUnknown => Alignment::AlignUnknown, | |
| } | |
| } | |
| } | |
| } | |
| // FIXME: Rename? | |
| pub mod rust { | |
| use crate::ffi::ParseMode; | |
| use generic_format_parser::{Parser, Piece}; | |
| pub fn collect_pieces( | |
| input: &str, | |
| style: Option<usize>, | |
| snippet: Option<String>, | |
| append_newline: bool, | |
| parse_mode: ParseMode, | |
| ) -> Vec<Piece<'_>> { | |
| let converted_parse_mode = match parse_mode { | |
| ParseMode::Format => generic_format_parser::ParseMode::Format, | |
| ParseMode::InlineAsm => generic_format_parser::ParseMode::InlineAsm, | |
| }; | |
| let parser = Parser::new(input, style, snippet, append_newline, converted_parse_mode); | |
| parser.into_iter().collect() | |
| } | |
| } | |
| // TODO: Should we instead make an FFIVector struct? | |
| type PieceVec<'a> = ffi::FFIVec<ffi::Piece<'a>>; | |
| #[no_mangle] | |
| pub extern "C" fn collect_pieces<'a>( | |
| input: ffi::RustHamster<'a>, | |
| append_newline: bool, | |
| parse_mode: crate::ffi::ParseMode, | |
| ) -> PieceVec<'a> { | |
| // FIXME: No unwrap | |
| let pieces: Vec<ffi::Piece<'_>> = | |
| rust::collect_pieces(input.as_str(), None, None, append_newline, parse_mode) | |
| .into_iter() | |
| .map(Into::into) | |
| .collect(); | |
| pieces.into_ffi() | |
| } | |
| #[no_mangle] | |
| pub extern "C" fn clone_pieces<'a, 'b>( | |
| piece_vec: &'a PieceVec<'b> | |
| ) -> PieceVec<'b> { | |
| piece_vec.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; | |
| } | |
| 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 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) | |
| ) | |
| } | |
| } |