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
(** JavaScript Number built-in object
This module mirrors the JavaScript Number API with prototype methods for
number-to-string conversion. *)
module Dtoa = Quickjs_c.Dtoa
module Cutils = Quickjs_c.Cutils
let js_dtoa_format_free = 0
let js_dtoa_format_fixed = 1
let js_dtoa_format_frac = 2
let js_dtoa_exp_auto = 0
let js_dtoa_exp_enabled = 1 lsl 2
let js_dtoa_exp_disabled = 2 lsl 2
let js_dtoa_minus_zero = 1 lsl 4
let max_digits = 101
let js_max_digits = 100
let max_int_buf_size = 65
(** Number of digits to use *)
type format =
| Free (** Use minimum digits needed for exact round-trip *)
| Fixed of int (** Use n significant digits (1-101) *)
| Fractional of int (** Use n fractional digits (0-101) *)
(** Exponential notation control *)
type exponent =
| Auto (** Use exponential when appropriate *)
| Always (** Always use exponential notation *)
| Never (** Never use exponential notation *)
type options = {
format : format;
exponent : exponent;
radix : int;
show_minus_zero : bool;
}
let default_options =
{ format = Free; exponent = Auto; radix = 10; show_minus_zero = false }
let validate_radix radix =
if radix < 2 || radix > 36 then
invalid_arg
(Printf.sprintf "Number: radix must be between 2 and 36, got %d" radix)
let validate_options options =
validate_radix options.radix;
(match options.format with
| Free -> ()
| Fixed n ->
if n < 1 || n > max_digits then
invalid_arg
(Printf.sprintf
"Number: Fixed digits must be between 1 and %d, got %d" max_digits
n)
| Fractional n ->
if n < 0 || n > max_digits then
invalid_arg
(Printf.sprintf
"Number: Fractional digits must be between 0 and %d, got %d"
max_digits n));
match (options.format, options.radix) with
| Free, _ | _, 10 -> ()
| (Fixed _ | Fractional _), radix ->
invalid_arg
(Printf.sprintf
"Number: radix %d is only supported with the Free format (the C \
implementation restricts fixed/fractional formatting to radix 10)"
radix)
let options_to_flags options =
let format_flag =
match options.format with
| Free -> js_dtoa_format_free
| Fixed _ -> js_dtoa_format_fixed
| Fractional _ -> js_dtoa_format_frac
in
let exp_flag =
match options.exponent with
| Auto -> js_dtoa_exp_auto
| Always -> js_dtoa_exp_enabled
| Never -> js_dtoa_exp_disabled
in
let minus_zero_flag =
if options.show_minus_zero then js_dtoa_minus_zero else 0
in
format_flag lor exp_flag lor minus_zero_flag
let n_digits_from_format format =
match format with Free -> 0 | Fixed n -> n | Fractional n -> n
module Prototype = struct
let to_string ?(options = default_options) d =
validate_options options;
let n_digits = n_digits_from_format options.format in
let flags = options_to_flags options in
let max_len = Dtoa.max_len d options.radix n_digits flags in
let buf = Ctypes.allocate_n Ctypes.char ~count:(max_len + 1) in
let tmp_mem = Ctypes.allocate_n Ctypes.uint64_t ~count:37 in
let tmp_mem_ptr = Ctypes.to_voidp tmp_mem in
let actual_len =
Dtoa.to_string buf d options.radix n_digits flags tmp_mem_ptr
in
Ctypes.string_from_ptr buf ~length:actual_len
let to_fixed n d =
if n < 0 || n > js_max_digits then
invalid_arg
(Printf.sprintf
"Number.to_fixed: digits must be between 0 and %d, got %d"
js_max_digits n);
to_string
~options:
{
format = Fractional n;
exponent = Never;
radix = 10;
show_minus_zero = false;
}
d
let to_precision n d =
if n < 1 || n > js_max_digits then
invalid_arg
(Printf.sprintf
"Number.to_precision: precision must be between 1 and %d, got %d"
js_max_digits n);
to_string
~options:
{
format = Fixed n;
exponent = Auto;
radix = 10;
show_minus_zero = false;
}
d
let to_exponential n d =
if n < 0 || n > js_max_digits then
invalid_arg
(Printf.sprintf
"Number.to_exponential: digits must be between 0 and %d, got %d"
js_max_digits n);
to_string
~options:
{
format = Fixed (n + 1);
exponent = Always;
radix = 10;
show_minus_zero = false;
}
d
let to_radix radix d =
validate_radix radix;
to_string
~options:
{ format = Free; exponent = Auto; radix; show_minus_zero = false }
d
end
let of_int32 n =
let buf = Ctypes.allocate_n Ctypes.char ~count:max_int_buf_size in
let len = Cutils.i32toa buf n in
Ctypes.string_from_ptr buf ~length:(Unsigned.Size_t.to_int len)
let of_uint32 n =
let buf = Ctypes.allocate_n Ctypes.char ~count:max_int_buf_size in
let len = Cutils.u32toa buf n in
Ctypes.string_from_ptr buf ~length:(Unsigned.Size_t.to_int len)
let of_int64 n =
let buf = Ctypes.allocate_n Ctypes.char ~count:max_int_buf_size in
let len = Cutils.i64toa buf n in
Ctypes.string_from_ptr buf ~length:(Unsigned.Size_t.to_int len)
let of_uint64 n =
let buf = Ctypes.allocate_n Ctypes.char ~count:max_int_buf_size in
let len = Cutils.u64toa buf n in
Ctypes.string_from_ptr buf ~length:(Unsigned.Size_t.to_int len)
let of_int n = of_int64 (Int64.of_int n)
let of_int32_radix ~radix n =
validate_radix radix;
let buf = Ctypes.allocate_n Ctypes.char ~count:max_int_buf_size in
let len = Cutils.i64toa_radix buf (Int64.of_int32 n) radix in
Ctypes.string_from_ptr buf ~length:(Unsigned.Size_t.to_int len)
let of_int64_radix ~radix n =
validate_radix radix;
let buf = Ctypes.allocate_n Ctypes.char ~count:max_int_buf_size in
let len = Cutils.i64toa_radix buf n radix in
Ctypes.string_from_ptr buf ~length:(Unsigned.Size_t.to_int len)
let of_int_radix ~radix n = of_int64_radix ~radix (Int64.of_int n)