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
module type IntSig = sig
type t
val name : string
val fmt : string
val zero : t
val max_int : t
val min_int : t
val bits : int
val of_int : int -> t
val to_int : t -> int
val add : t -> t -> t
val sub : t -> t -> t
val mul : t -> t -> t
val divmod : t -> t -> t * t
end
module type S = sig
type t
val of_substring : string -> pos:int -> (t * int)
val of_string : string -> t
val to_string : t -> string
val to_string_bin : t -> string
val to_string_oct : t -> string
val to_string_hex : t -> string
val printer : Format.formatter -> t -> unit
val printer_bin : Format.formatter -> t -> unit
val printer_oct : Format.formatter -> t -> unit
val printer_hex : Format.formatter -> t -> unit
end
module Make (I : IntSig) : S with type t = I.t = struct
type t = I.t
exception EndOfNumber of I.t * int
(** Base function for *of_string* and *of_substring*
* functions *)
let _of_substring start_off s func_name =
let fail () = invalid_arg (I.name ^ func_name) in
if start_off >= String.length s then fail ();
let negative, off =
if s.[start_off] = '-' then
true, 1+start_off
else if s.[start_off] = '+' then
false, 1+start_off
else
false, start_off in
let len = String.length s in
if len <= off then fail ();
let base, off =
let is_digit ~base c =
if base <= 10 then (
Char.(code c - code '0') < base
) else (
(c >= '0' && c <= '9') ||
(10 + Char.(code (lowercase_ascii c) - code 'a') < base)
) in
if len - off < 3 then
10, off
else if s.[off] = '0' then
match Char.lowercase_ascii s.[off + 1] with
| 'b' when is_digit ~base:2 s.[off+2] -> 2, off + 2
| 'o' when is_digit ~base:8 s.[off+2] -> 8, off + 2
| 'x' when is_digit ~base:16 s.[off+2] -> 16, off + 2
| _ -> 10, off
else 10, off in
let base = I.of_int base in
let (thresh, rem), join, cmp_safe =
if negative then
(I.divmod I.min_int base, I.sub, 1)
else
(I.divmod I.max_int base, I.add, -1) in
let rec loop off (n : I.t) =
if off = len then
n, off
else begin
let c = s.[off] in
if c <> '_' then begin
let disp =
if c >= '0' && c <= '9' then 48
else if c >= 'A' && c <= 'F' then 55
else if c >= 'a' && c <= 'f' then 87
else raise (EndOfNumber (n, off)) in
let disp = int_of_char c - disp in
let d = I.of_int disp in
if d >= base then raise (EndOfNumber (n, off));
(match compare n thresh with
| 0 ->
let r = compare d rem in
if r <> cmp_safe && r <> 0 then raise (EndOfNumber (n, off));
| r ->
if r <> cmp_safe then raise (EndOfNumber (n, off)));
let res = join (I.mul n base) d in
loop (off + 1) res
end else
loop (off + 1) n
end
in
loop off I.zero
let of_substring s ~pos =
try
_of_substring pos s ".of_substring"
with
| EndOfNumber (n, off) -> n, off
let of_string s =
try
let n, _ = _of_substring 0 s ".of_string" in n
with
| EndOfNumber _ -> invalid_arg (I.name ^ ".of_string")
let to_string_base base prefix x =
let prefixlen = String.length prefix in
let base = I.of_int base in
let conv = "0123456789abcdef" in
if x = I.zero then
prefix ^ "0"
else begin
let maxlen = 1 + prefixlen + I.bits in
let buffer = Bytes.create maxlen in
let off = ref (maxlen - 1) in
let rec loop n =
if n <> I.zero then begin
let n', digit = I.divmod n base in
let digit = (I.to_int digit) in
Bytes.set buffer !off conv.[abs digit];
decr off;
loop n'
end in
loop x;
for i = prefixlen - 1 downto 0 do
Bytes.set buffer !off (String.get prefix i);
decr off
done;
if x < I.zero then begin
Bytes.set buffer !off '-';
decr off
end;
Bytes.sub_string buffer (!off + 1) (maxlen - !off - 1)
end
let to_string = to_string_base 10 ""
let to_string_bin = to_string_base 2 "0b"
let to_string_oct = to_string_base 8 "0o"
let to_string_hex = to_string_base 16 "0x"
let print_with f fmt x =
Format.fprintf fmt "@[%s@]" (f x ^ I.fmt)
let printer = print_with to_string
let printer_bin = print_with to_string_bin
let printer_oct = print_with to_string_oct
let printer_hex = print_with to_string_hex
end