use crate::Interop;
use crate::converter::{is_reusable, param_to_type, vec_t};
use crate::utils::{MoveSemantics, write_common_marshaller};
use interoptopus::lang::{Parameter, Type};
use interoptopus::pattern::TypePattern;
use interoptopus::pattern::vec::VecType;
use interoptopus_backend_utils::{Error, IndentWriter, indented};
pub fn write_pattern_vec(i: &Interop, w: &mut IndentWriter, vec: &VecType) -> Result<(), Error> {
i.debug(w, "write_pattern_vec")?;
if is_reusable(vec.t()) {
write_pattern_fast_vec(i, w, vec)
} else {
write_pattern_marshalling_vec(i, w, vec)
}
}
pub fn write_pattern_fast_vec(i: &Interop, w: &mut IndentWriter, vec: &VecType) -> Result<(), Error> {
i.debug(w, "write_pattern_fast_vec")?;
let name = vec.rust_name();
let the_type = param_to_type(vec.t());
write_pattern_vec_struct(i, w, vec)?;
indented!(w, r"[NativeMarshalling(typeof(MarshallerMeta))]")?;
indented!(w, r"public partial class {} : IDisposable", name)?;
indented!(w, r"{{")?;
w.indent();
indented!(w, r"// An internal helper to create an empty object.")?;
i.inline_hint(w, 0)?;
indented!(w, r"private {name}() {{ }}")?;
w.newline()?;
i.inline_hint(w, 0)?;
indented!(w, r"public static unsafe {name} From(Span<{the_type}> _data)")?;
indented!(w, r"{{")?;
indented!(w, [()], r"var rval = new {name}();")?;
indented!(w, [()], r"fixed (void* _data_ptr = _data)")?;
indented!(w, [()], r"{{")?;
indented!(w, [()()], r"InteropHelper.interoptopus_vec_create((IntPtr) _data_ptr, (ulong)_data.Length, out var _out);")?;
indented!(w, [()()], r"rval._len = _out._len;")?;
indented!(w, [()()], r"rval._capacity = _out._capacity;")?;
indented!(w, [()()], r"rval._ptr = _out._ptr;")?;
indented!(w, [()], r"}}")?;
indented!(w, [()], r"return rval;")?;
indented!(w, r"}}")?;
w.newline()?;
i.inline_hint(w, 0)?;
indented!(w, r"public static unsafe {name} Empty()")?;
indented!(w, r"{{")?;
indented!(w, [()], r"InteropHelper.interoptopus_vec_create(IntPtr.Zero, 0, out var _out);")?;
indented!(w, [()], r"return _out.IntoManaged();")?;
indented!(w, r"}}")?;
w.newline()?;
indented!(w, r"public int Count")?;
indented!(w, r"{{")?;
i.inline_hint(w, 1)?;
indented!(w, [()], r"get {{ if (_ptr == IntPtr.Zero) {{ throw new NullReferenceException(); }} else {{ return (int) _len; }} }}")?;
indented!(w, r"}}")?;
w.newline()?;
indented!(w, r"public unsafe {} this[int i]", the_type)?;
indented!(w, r"{{")?;
w.indent();
i.inline_hint(w, 0)?;
indented!(w, r"get")?;
indented!(w, r"{{")?;
indented!(w, [()], r"if (i >= Count) throw new IndexOutOfRangeException();")?;
indented!(w, [()], r"if (_ptr == IntPtr.Zero) throw new NullReferenceException();")?;
indented!(w, [()], r"return Marshal.PtrToStructure<{}>(new IntPtr(_ptr.ToInt64() + i * sizeof({})));", the_type, the_type)?;
indented!(w, r"}}")?;
w.unindent();
indented!(w, r"}}")?;
w.newline()?;
write_pattern_vec_to_unmanaged(i, w)?;
w.newline()?;
write_pattern_vec_helpers(i, w, vec)?;
w.newline()?;
write_pattern_vec_interop_helper(i, w, vec)?;
w.newline()?;
indented!(w, r"[CustomMarshaller(typeof({}), MarshalMode.Default, typeof(Marshaller))]", name)?;
indented!(w, r"private struct MarshallerMeta {{ }}")?;
w.newline()?;
indented!(w, r"[StructLayout(LayoutKind.Sequential)]")?;
indented!(w, r"public struct Unmanaged")?;
indented!(w, r"{{")?;
indented!(w, [()], r"internal IntPtr _ptr;")?;
indented!(w, [()], r"internal ulong _len;")?;
indented!(w, [()], r"internal ulong _capacity;")?;
w.newline()?;
write_pattern_vec_to_managed(i, w, name)?;
w.newline()?;
indented!(w, r"}}")?;
w.newline()?;
write_common_marshaller(i, w, name, MoveSemantics::Move)?;
w.unindent();
indented!(w, r"}}")?;
w.newline()?;
indented!(w, r"public static class {name}Extensions")?;
indented!(w, r"{{")?;
indented!(w, [()], r"public static {name} Vec(this {the_type}[] s) {{ return {name}.From(s); }}")?;
indented!(w, r"}}")?;
w.newline()?;
Ok(())
}
pub fn write_pattern_marshalling_vec(i: &Interop, w: &mut IndentWriter, vec: &VecType) -> Result<(), Error> {
i.debug(w, "write_pattern_fast_vec")?;
let name = vec.rust_name();
let the_type = vec_t(vec);
write_pattern_vec_struct(i, w, vec)?;
indented!(w, r"[NativeMarshalling(typeof(MarshallerMeta))]")?;
indented!(w, r"public partial class {} : IDisposable", name)?;
indented!(w, r"{{")?;
w.indent();
indented!(w, r"// An internal helper to create an empty object.")?;
i.inline_hint(w, 0)?;
indented!(w, r"private {name}() {{ }}")?;
w.newline()?;
i.inline_hint(w, 0)?;
indented!(w, r"public static unsafe {name} From(Span<{the_type}> _data)")?;
indented!(w, r"{{")?;
indented!(w, [()], r"var _temp = new {the_type}.Unmanaged[_data.Length];")?;
indented!(w, [()], r"for (var i = 0; i < _data.Length; ++i)")?;
indented!(w, [()], r"{{")?;
indented!(w, [()()], r"_temp[i] = _data[i].IntoUnmanaged();")?;
indented!(w, [()], r"}}")?;
indented!(w, [()], r"fixed (void* _data_ptr = _temp)")?;
indented!(w, [()], r"{{")?;
indented!(w, [()()], r"InteropHelper.interoptopus_vec_create((IntPtr) _data_ptr, (ulong)_data.Length, out var _out);")?;
indented!(w, [()()], r"return _out.IntoManaged();")?;
indented!(w, [()], r"}}")?;
indented!(w, r"}}")?;
w.newline()?;
i.inline_hint(w, 0)?;
indented!(w, r"public static unsafe {name} Empty()")?;
indented!(w, r"{{")?;
indented!(w, [()], r"InteropHelper.interoptopus_vec_create(IntPtr.Zero, 0, out var _out);")?;
indented!(w, [()], r"return _out.IntoManaged();")?;
indented!(w, r"}}")?;
w.newline()?;
indented!(w, r"public int Count")?;
indented!(w, r"{{")?;
i.inline_hint(w, 1)?;
indented!(w, [()], r"get {{ if (_ptr == IntPtr.Zero) {{ throw new NullReferenceException(); }} else {{ return (int) _len; }} }}")?;
indented!(w, r"}}")?;
w.newline()?;
indented!(w, r"public unsafe {the_type} this[int i]")?;
indented!(w, r"{{")?;
w.indent();
i.inline_hint(w, 0)?;
indented!(w, r"get")?;
indented!(w, r"{{")?;
indented!(w, [()], r"if (i >= Count) throw new IndexOutOfRangeException();")?;
indented!(w, [()], r"if (_ptr == IntPtr.Zero) throw new NullReferenceException();")?;
indented!(w, [()], r"var _element = Marshal.PtrToStructure<{the_type}.Unmanaged>(new IntPtr(_ptr.ToInt64() + i * sizeof({the_type}.Unmanaged)));")?;
indented!(w, [()], r"return _element.IntoManaged();")?;
indented!(w, r"}}")?;
w.unindent();
indented!(w, r"}}")?;
write_pattern_vec_to_unmanaged(i, w)?;
w.newline()?;
write_pattern_vec_helpers(i, w, vec)?;
w.newline()?;
write_pattern_vec_interop_helper(i, w, vec)?;
w.newline()?;
indented!(w, r"[CustomMarshaller(typeof({}), MarshalMode.Default, typeof(Marshaller))]", name)?;
indented!(w, r"private struct MarshallerMeta {{ }}")?;
w.newline()?;
indented!(w, r"[StructLayout(LayoutKind.Sequential)]")?;
indented!(w, r"public struct Unmanaged")?;
indented!(w, r"{{")?;
indented!(w, [()], r"internal IntPtr _ptr;")?;
indented!(w, [()], r"internal ulong _len;")?;
indented!(w, [()], r"internal ulong _capacity;")?;
write_pattern_vec_to_managed(i, w, name)?;
w.newline()?;
indented!(w, r"}}")?;
w.newline()?;
write_common_marshaller(i, w, name, MoveSemantics::Move)?;
w.unindent();
w.newline()?;
indented!(w, r"}}")?;
w.newline()?;
indented!(w, r"public static class {name}Extensions")?;
indented!(w, r"{{")?;
i.inline_hint(w, 1)?;
indented!(w, [()], r"public static {name} IntoVec(this {the_type}[] s) {{ return {name}.From(s); }}")?;
indented!(w, r"}}")?;
w.newline()?;
Ok(())
}
pub fn write_pattern_vec_struct(_: &Interop, w: &mut IndentWriter, vec: &VecType) -> Result<(), Error> {
let name = vec.rust_name();
indented!(w, r"// This must be a class because we only ever want to hold on to the")?;
indented!(w, r"// same instance, as we overwrite fields when this is sent over the FFI")?;
indented!(w, r"// boundary")?;
indented!(w, r"public partial class {}", name)?;
indented!(w, r"{{")?;
indented!(w, [()], r"internal IntPtr _ptr;")?;
indented!(w, [()], r"internal ulong _len;")?;
indented!(w, [()], r"internal ulong _capacity;")?;
indented!(w, r"}}")?;
w.newline()?;
Ok(())
}
pub fn write_pattern_vec_helpers(i: &Interop, w: &mut IndentWriter, v: &VecType) -> Result<(), Error> {
let name = v.rust_name();
i.inline_hint(w, 0)?;
indented!(w, r"public void Dispose()")?;
indented!(w, r"{{")?;
indented!(w, [()], r"if (_ptr == IntPtr.Zero) return;")?;
indented!(w, [()], r"var _unmanaged = new Unmanaged();")?;
indented!(w, [()], r"_unmanaged._ptr = _ptr;")?;
indented!(w, [()], r"_unmanaged._len = _len;")?;
indented!(w, [()], r"_unmanaged._capacity = _capacity;")?;
indented!(w, [()], r"InteropHelper.interoptopus_vec_destroy(_unmanaged);")?;
indented!(w, [()], r"_ptr = IntPtr.Zero;")?;
indented!(w, [()], r"_len = 0;")?;
indented!(w, [()], r"_capacity = 0;")?;
indented!(w, r"}}")?;
w.newline()?;
i.inline_hint(w, 0)?;
indented!(w, r"public override string ToString()")?;
indented!(w, r"{{")?;
indented!(w, [()], r#"return "{name} {{ ... }}";"#)?;
indented!(w, r"}}")?;
w.newline()?;
Ok(())
}
#[allow(clippy::collapsible_if)]
pub fn write_pattern_vec_interop_helper(i: &Interop, w: &mut IndentWriter, vec: &VecType) -> Result<(), Error> {
indented!(w, r"public partial class InteropHelper")?;
indented!(w, r"{{")?;
for f in i.inventory.functions() {
let first_param = f.signature().params().first().map(Parameter::the_type).cloned();
let last_param = f.signature().params().last().map(Parameter::the_type).cloned();
let name = f.name();
let extra_fn_decorations = i.fn_decorations();
if name.starts_with("interoptopus_vec_create") {
if let Some(Type::ReadWritePointer(x)) = last_param {
if let Type::Pattern(TypePattern::Vec(x)) = x.as_ref() {
if x == vec {
indented!(w, [()], r#"[LibraryImport(Interop.NativeLib, EntryPoint = "{name}")]"#)?;
for decor in &extra_fn_decorations {
indented!(w, [()], "{}", decor)?;
}
indented!(w, [()], r"internal static partial long interoptopus_vec_create(IntPtr vec, ulong len, out Unmanaged rval);")?;
}
}
}
}
if name.starts_with("interoptopus_vec_destroy") && first_param == Some(Type::Pattern(TypePattern::Vec(vec.clone()))) {
indented!(w, [()], r#"[LibraryImport(Interop.NativeLib, EntryPoint = "{name}")]"#)?;
for decor in &extra_fn_decorations {
indented!(w, [()], "{}", decor)?;
}
indented!(w, [()], r"internal static partial long interoptopus_vec_destroy(Unmanaged vec);")?;
}
}
indented!(w, r"}}")?;
Ok(())
}
pub fn write_pattern_vec_to_managed(i: &Interop, w: &mut IndentWriter, managed: &str) -> Result<(), Error> {
i.inline_hint(w, 1)?;
indented!(w, [()], r"public {managed} IntoManaged()")?;
indented!(w, [()], r"{{")?;
indented!(w, [()()], r"var rval = new {managed}();")?;
indented!(w, [()()], r"rval._len = _len;")?;
indented!(w, [()()], r"rval._capacity = _capacity;")?;
indented!(w, [()()], r"rval._ptr = _ptr;")?;
indented!(w, [()()], r"return rval;")?;
indented!(w, [()], r"}}")?;
w.newline()?;
Ok(())
}
pub fn write_pattern_vec_to_unmanaged(i: &Interop, w: &mut IndentWriter) -> Result<(), Error> {
i.inline_hint(w, 0)?;
indented!(w, r"public Unmanaged IntoUnmanaged()")?;
indented!(w, r"{{")?;
indented!(w, [()], r"if (_ptr == IntPtr.Zero) throw new NullReferenceException(); // Don't use for serialization if moved already.")?;
indented!(w, [()], r"var rval = new Unmanaged();")?;
indented!(w, [()], r"rval._len = _len;")?;
indented!(w, [()], r"rval._capacity = _capacity;")?;
indented!(w, [()], r"rval._ptr = _ptr;")?;
indented!(w, [()], r"_ptr = IntPtr.Zero; // Mark this instance as moved.")?;
indented!(w, [()], r"return rval;")?;
indented!(w, r"}}")?;
w.newline()?;
i.inline_hint(w, 0)?;
indented!(w, r"public Unmanaged AsUnmanaged()")?;
indented!(w, r"{{")?;
indented!(w, [()], r"if (_ptr == IntPtr.Zero) throw new NullReferenceException(); // Don't use for serialization if moved already.")?;
indented!(w, [()], r"var rval = new Unmanaged();")?;
indented!(w, [()], r"rval._len = _len;")?;
indented!(w, [()], r"rval._capacity = _capacity;")?;
indented!(w, [()], r"rval._ptr = _ptr;")?;
indented!(w, [()], r"return rval;")?;
indented!(w, r"}}")?;
Ok(())
}