1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// Copyright (c) 2020-2022  David Sorokin <david.sorokin@gmail.com>, based in Yoshkar-Ola, Russia
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use std::mem;

use libc::*;

use crate::simulation::Run;

/// Represents an undoable action.
pub struct UndoableActionBox {
    f: Box<dyn UndoableActionFnBox>
}

impl UndoableActionBox {

    /// Create a new boxed computation.
    #[doc(hidden)]
    #[inline]
    pub fn new<F>(f: F) -> Self
        where F: FnOnce(&Run) + 'static
    {
        UndoableActionBox {
            f: Box::new(f)
        }
    }

    /// Call the boxed function.
    #[doc(hidden)]
    #[inline]
    pub fn call_box(self, arg: (&Run,)) {
        let UndoableActionBox { f } = self;
        f.call_box(arg)
    }
}

/// A trait to support the stable version of Rust, where there is no `FnBox`.
trait UndoableActionFnBox {

    /// Call the corresponding function.
    fn call_box(self: Box<Self>, args: (&Run,));
}

impl<F> UndoableActionFnBox for F
    where F: for<'a> FnOnce(&'a Run)
{
    fn call_box(self: Box<Self>, args: (&Run,)) {
        let this: Self = *self;
        this(args.0)
    }
}

/// It represents a raw trait object.
#[repr(C)]
#[derive(Copy, Clone)]
struct UndoableActionTraitObject {

    field1: *mut c_void,
    field2: *mut c_void
}

/// A C-friendly representaton of the `UndoableActionBox` action.
#[repr(C)]
pub struct UndoableActionRepr {

    /// Delete the object.
    delete: unsafe extern "C" fn(obj: *mut UndoableActionTraitObject),

    /// The callback.
    callback: unsafe extern "C" fn(obj: *mut UndoableActionTraitObject, r: *const Run),

    /// The trait object.
    trait_object: UndoableActionTraitObject
}

impl Drop for UndoableActionRepr {

    fn drop(&mut self) {
        unsafe {
            (self.delete)(&mut self.trait_object);
        }
    }
}

impl UndoableActionRepr {

    /// Convert to a C-friendly representation.
    #[inline]
    pub fn into_repr(comp: UndoableActionBox) -> UndoableActionRepr {
        unsafe {
            UndoableActionRepr {
                delete: delete_undoable_action_repr,
                callback: call_undoable_action_repr,
                trait_object: mem::transmute(comp)
            }
        }
    }

    /// Call the representation.
    #[inline]
    fn _call_repr(mut self, r: &Run) {
        unsafe {
            (self.callback)(&mut self.trait_object, r);
            mem::forget(self);
        }
    }
}

/// Call the `UndoableActionBox` representation.
unsafe extern "C" fn call_undoable_action_repr(comp: *mut UndoableActionTraitObject, r: *const Run) {
    let comp: UndoableActionBox = mem::transmute(*comp);
    comp.call_box((&*r,));
}

/// Delete the `UndoableActionBox` representation.
unsafe extern "C" fn delete_undoable_action_repr(comp: *mut UndoableActionTraitObject) {
    let _: UndoableActionBox = mem::transmute(*comp);
}

/// Represents a log of undoable actions.
pub type UndoableLog = c_void;

#[cfg_attr(windows, link(name = "dvcompute_core_dist.dll"))]
#[cfg_attr(not(windows), link(name = "dvcompute_core_dist"))]
extern {

    /// Create a new undoable log.
    #[doc(hidden)]
    pub fn create_extern_undoable_log() -> *mut UndoableLog;

    /// Delete the undoable log.
    #[doc(hidden)]
    pub fn delete_extern_undoable_log(log: *mut UndoableLog);

    /// Write the new action at the specified modeling time.
    #[doc(hidden)]
    pub fn extern_write_log(log: *mut UndoableLog, t: f64, action: UndoableActionRepr);
}