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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
use std::{
hash::{Hash, Hasher},
os::raw::c_char,
};
#[repr(C)]
#[derive(Debug, Copy, Clone)]
#[allow(non_camel_case_types)]
pub struct decNumber {
pub digits: i32,
pub exponent: i32,
pub bits: u8,
pub lsu: [u16; DECNUMUNITS as _],
}
impl Hash for decNumber {
#[inline(always)]
fn hash<H: Hasher>(&self, state: &mut H) {
self.digits.hash(state);
self.exponent.hash(state);
self.bits.hash(state);
for i in 0..self.digits as usize / DECDPUN {
self.lsu[i].hash(state);
}
}
}
pub const DECDPUN: usize = 3;
pub const DECNUMUNITS: u32 = 13;
pub const DECIMAL_MAX_DIGITS: u32 = 38;
pub const MP_DECIMAL: i8 = 1;
crate::define_dlsym_reloc! {
/// Return decimal precision,
/// i.e. the amount of decimal digits in
/// its representation.
pub fn decimal_precision(dec: *const decNumber) -> ::std::os::raw::c_int;
/// Return decimal scale,
/// i.e. the number of decimal digits after
/// the decimal separator.
pub fn decimal_scale(dec: *const decNumber) -> ::std::os::raw::c_int;
/// Initialize a zero decimal number.
pub fn decimal_zero(dec: *mut decNumber) -> *mut decNumber;
/// Return `true` if the fractional part of the number is `0`,
/// `false` otherwise.
pub fn decimal_is_int(dec: *const decNumber) -> bool;
/// Initialize a decimal with a value from the string.
///
/// If the number is less, than `10^DECIMAL_MAX_DIGITS`,
/// but has excess digits in fractional part, it will be rounded.
///
/// Return `NULL` if string is invalid or
/// the number is too big (>= `10^DECIMAL_MAX_DIGITS`)
pub fn decimal_from_string(
dec: *mut decNumber,
s: *const ::std::os::raw::c_char,
) -> *mut decNumber;
/// Initialize a decimal with a value from the valid beginning
/// of the string.
/// If `endptr` is not `NULL`, store the address of the first
/// invalid character in *endptr.
///
/// If the number is less, than `10^DECIMAL_MAX_DIGITS`,
/// but has excess digits in fractional part, it will be rounded.
///
/// Return `NULL` if string is invalid or
/// the number is too big (>= `10^DECIMAL_MAX_DIGITS`)
pub fn strtodec(
dec: *mut decNumber,
s: *const ::std::os::raw::c_char,
endptr: *mut *const ::std::os::raw::c_char,
) -> *mut decNumber;
/// Initialize a decimal from double.
///
/// Return
/// - `NULL` if double is `NaN` or `Infinity`,
/// or is greater than `10^DECIMAL_MAX_DIGITS`.
/// - `dec` otherwise.
pub fn decimal_from_double(dec: *mut decNumber, d: f64) -> *mut decNumber;
/// Initialize a decimal with an integer value.
pub fn decimal_from_int64(dec: *mut decNumber, num: i64) -> *mut decNumber;
/// Initialize a decimal with an integer value.
pub fn decimal_from_uint64(dec: *mut decNumber, num: u64) -> *mut decNumber;
/// `dn` is the decNumber to convert
/// `string` is the string where the result will be laid out
///
/// `string` must be at least `digits count + 14` characters long
///
/// No error is possible, and no status can be set.
#[allow(non_snake_case)]
pub fn decNumberToString(dn: *const decNumber, string: *mut c_char) -> *mut c_char;
/// Convert a given decimal to `i64`.
/// `num` - the result.
/// Return `NULL` if `dec` doesn't fit into `i64`
pub fn decimal_to_int64(dec: *const decNumber, num: *mut i64) -> *const decNumber;
/// Convert a given decimal to `u64`.
/// `num` - the result.
/// Return `NULL` if `dec` doesn't fit into `u64`
pub fn decimal_to_uint64(dec: *const decNumber, num: *mut u64) -> *const decNumber;
/// Compare 2 decimal values.
/// Return
/// - -1, `lhs` < `rhs`,
/// - 0, `lhs` = `rhs`,
/// - 1, `lhs` > `rhs`
pub fn decimal_compare(lhs: *const decNumber, rhs: *const decNumber) -> ::std::os::raw::c_int;
/// Round a given decimal to have not more than
/// `scale` digits after the decimal point.
/// If `scale` if greater than current `dec` scale, do nothing.
/// Scale must be in range `[0, DECIMAL_MAX_DIGITS]`
///
/// Return `NULL`, if scale is out of bounds.
pub fn decimal_round(dec: *mut decNumber, scale: ::std::os::raw::c_int) -> *mut decNumber;
/// Round a decimal towards zero.
/// See also [`decimal_round`]
pub fn decimal_floor(dec: *mut decNumber, scale: ::std::os::raw::c_int) -> *mut decNumber;
/// Remove trailing zeros from the fractional part of a number.
/// Return `dec` with trimmed fractional zeros.
pub fn decimal_trim(dec: *mut decNumber) -> *mut decNumber;
/// Set scale of `dec` to `scale`.
/// If `scale` < `scale(dec)`,
/// performs [`decimal_round`]`()`.
/// Otherwise appends a sufficient amount of trailing
/// fractional zeros.
/// Return
/// - `NULL`, scale < `0` or too big.
/// - `dec` with set scale otherwise.
pub fn decimal_rescale(dec: *mut decNumber, scale: ::std::os::raw::c_int) -> *mut decNumber;
/// `res` is set to the remainder of dividing `lhs` by `rhs`.
pub fn decimal_remainder(
res: *mut decNumber,
lhs: *const decNumber,
rhs: *const decNumber,
) -> *mut decNumber;
/// `res` is set to the absolute value of `dec`
/// `decimal_abs(&a, &a)` is allowed.
pub fn decimal_abs(res: *mut decNumber, dec: *const decNumber) -> *mut decNumber;
/// `res` is set to `-dec`.
pub fn decimal_minus(res: *mut decNumber, dec: *const decNumber) -> *mut decNumber;
pub fn decimal_add(
res: *mut decNumber,
lhs: *const decNumber,
rhs: *const decNumber,
) -> *mut decNumber;
pub fn decimal_sub(
res: *mut decNumber,
lhs: *const decNumber,
rhs: *const decNumber,
) -> *mut decNumber;
pub fn decimal_mul(
res: *mut decNumber,
lhs: *const decNumber,
rhs: *const decNumber,
) -> *mut decNumber;
pub fn decimal_div(
res: *mut decNumber,
lhs: *const decNumber,
rhs: *const decNumber,
) -> *mut decNumber;
pub fn decimal_log10(res: *mut decNumber, lhs: *const decNumber) -> *mut decNumber;
pub fn decimal_ln(res: *mut decNumber, lhs: *const decNumber) -> *mut decNumber;
pub fn decimal_pow(
res: *mut decNumber,
lhs: *const decNumber,
rhs: *const decNumber,
) -> *mut decNumber;
pub fn decimal_exp(res: *mut decNumber, lhs: *const decNumber) -> *mut decNumber;
pub fn decimal_sqrt(res: *mut decNumber, lhs: *const decNumber) -> *mut decNumber;
/// Return The length in bytes decimal packed representation will take.
pub fn decimal_len(dec: *const decNumber) -> u32;
/// Convert a decimal `dec` to its packed representation.
///
/// Return `data + `[`decimal_len`]`(dec)`;
pub fn decimal_pack(
data: *mut ::std::os::raw::c_char,
dec: *const decNumber,
) -> *mut ::std::os::raw::c_char;
/// Using a packed representation of size `len` pointed to by
/// `*data`, unpack it to `dec`.
///
/// Advances `data`: `*data = *data + `[`decimal_len`]`(dec);`
///
/// Return
/// - `NULL` if value encoding is incorrect
/// - `dec` otherwise.
pub fn decimal_unpack(
data: *mut *const ::std::os::raw::c_char,
len: u32,
dec: *mut decNumber,
) -> *mut decNumber;
}
#[cfg(feature = "internal_test")]
mod test {
use super::*;
use crate::offset_of;
#[crate::test(tarantool = "crate")]
fn decimal_ffi_definition() {
if !crate::ffi::has_decimal() {
return;
}
let lua = crate::lua_state();
let [
size_of_decimal,
offset_of_digits,
offset_of_exponent,
offset_of_bits,
offset_of_lsu,
]: [usize; 5] = lua.eval(
"local ffi = require 'ffi'
return {
ffi.sizeof('decimal_t'),
ffi.offsetof('decimal_t', 'digits'),
ffi.offsetof('decimal_t', 'exponent'),
ffi.offsetof('decimal_t', 'bits'),
ffi.offsetof('decimal_t', 'lsu'),
}",
).unwrap();
// TODO: could also check the actual types of fields using
// `ffi.typeinfo`, but this requires more work
assert_eq!(size_of_decimal, std::mem::size_of::<decNumber>());
assert_eq!(offset_of_digits, offset_of!(decNumber, digits));
assert_eq!(offset_of_exponent, offset_of!(decNumber, exponent));
assert_eq!(offset_of_bits, offset_of!(decNumber, bits));
assert_eq!(offset_of_lsu, offset_of!(decNumber, lsu));
}
}