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
142
143
144
145
146
147
//! Contains OCaml types and conversion functions from runtime representations.
use crate::tag::Tag;

/// OCaml `value` type
pub type Value = isize;

/// OCaml's integer type
pub type Intnat = isize;

/// OCaml's unsigned integer type
pub type Uintnat = usize;

/// OCaml's size type
pub type Size = Uintnat;
pub type Color = Uintnat;
pub type Mark = Uintnat;

/// An OCaml heap-allocated block header. **NB**: this is currently unimplemented.
///
/// Structure of the header:
///
/// For 16-bit and 32-bit architectures:
///
///```text
///      +--------+-------+-----+
///      | wosize | color | tag |
///      +--------+-------+-----+
/// bits  31    10 9     8 7   0
///```
///
/// For 64-bit architectures:
///
///```text
///      +--------+-------+-----+
///      | wosize | color | tag |
///      +--------+-------+-----+
/// bits  63    10 9     8 7   0
///```
///
pub type Header = Uintnat;

/// #ifdef ARCH_BIG_ENDIAN
/// #define Tag_val(val) (((unsigned char *) (val)) [-1])
/// #else
/// #define Tag_val(val) (((unsigned char *) (val)) [-sizeof(value)])
/// #endif
#[cfg(target_endian = "big")]
#[inline]
pub const unsafe fn tag_val(val: Value) -> Tag {
    *(val as *const u8).offset(-1)
}

#[cfg(target_endian = "little")]
#[inline]
pub unsafe fn tag_val(val: Value) -> Tag {
    *(val as *const u8).offset(-(core::mem::size_of::<Value>() as isize))
}

#[inline]
pub unsafe fn hd_val(val: Value) -> Header {
    *(val as *const Header).offset(-1)
}

#[inline]
pub unsafe fn wosize_val(val: Value) -> Size {
    hd_val(val) >> 10
}

/// `(((intnat)(x) << 1) + 1)`
pub const unsafe fn val_int(i: isize) -> Value {
    ((i as isize) << 1) + 1
}

pub const unsafe fn int_val(val: Value) -> isize {
    ((val as isize) >> 1) as isize
}

pub fn is_block(v: Value) -> bool {
    (v & 1) == 0
}

pub const fn is_long(v: Value) -> bool {
    (v & 1) != 0
}

// #define Max_long (((intnat)1 << (8 * sizeof(value) - 2)) - 1)
// #define Min_long (-((intnat)1 << (8 * sizeof(value) - 2)))

/// Maximum possible value for an OCaml fixnum.
pub const MAX_FIXNUM: Intnat = (1 << (8 * core::mem::size_of::<Intnat>() - 2)) - 1;

/// Minimum possible value for an OCaml fixnum.
pub const MIN_FIXNUM: Intnat = -(1 << (8 * core::mem::size_of::<Intnat>() - 2));

/// Extract a field from an OCaml value
///
/// # Safety
///
/// This function does no bounds checking or validation of the OCaml values
pub unsafe fn field(block: Value, index: usize) -> *mut Value {
    (block as *mut Value).add(index)
}

/// The OCaml `()` (`unit`) value
pub const UNIT: Value = unsafe { val_int(0) };

/// The OCaml `None` value
pub const NONE: Value = unsafe { val_int(0) };

/// Empty list value
pub const EMPTY_LIST: Value = unsafe { val_int(0) };

/// The OCaml `true` value
pub const TRUE: Value = unsafe { val_int(1) };

/// OCaml `false` value
pub const FALSE: Value = unsafe { val_int(0) };

/// Tag used for OCaml conses
pub const TAG_CONS: Tag = 0;

/// Tag used for OCaml `Some x` values
pub const TAG_SOME: Tag = 0;

/// Pointer to the first byte
#[inline]
pub const unsafe fn bp_val(val: Value) -> *const u8 {
    val as *const u8
}

/// Extracts a machine `ptr` to the bytes making up an OCaml `string`
#[inline]
pub const unsafe fn string_val(val: Value) -> *mut u8 {
    val as *mut u8
}

extern "C" {
    /// Returns size of the string in `value` in bytes
    pub fn caml_string_length(value: Value) -> Size;
    pub fn caml_array_length(value: Value) -> Size;
    pub fn caml_hash_variant(tag: *const u8) -> Value;
    pub fn caml_get_public_method(obj: Value, tag: Value) -> Value;
    pub fn caml_sys_store_double_val(x: Value, f: f64);
    pub fn caml_sys_double_val(x: Value) -> f64;
    pub fn caml_sys_double_field(x: Value, i: Size) -> f64;
    pub fn caml_sys_store_double_field(x: Value, index: Size, d: f64);
}