2 * IO - Abstract input/output
3 * Copyright (C) 2003 Nicolas Cannasse
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version,
9 * with the special exception on linking described in file LICENSE.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 mutable in_read
: unit -> char
;
23 mutable in_input
: string -> int -> int -> int;
24 mutable in_close
: unit -> unit;
28 mutable out_write
: char
-> unit;
29 mutable out_output
: string -> int -> int -> int;
30 mutable out_close
: unit -> 'a
;
31 mutable out_flush
: unit -> unit;
34 exception No_more_input
35 exception Input_closed
36 exception Output_closed
38 (* -------------------------------------------------------------- *)
41 let default_close = (fun () -> ())
43 let create_in ~read ~input ~close
=
50 let create_out ~write ~output ~flush ~close
=
58 let read i
= i
.in_read
()
61 if n
< 0 then invalid_arg
"IO.nread";
65 let s = String.create n
in
70 let r = i
.in_input
s !p !l in
71 if r = 0 then raise No_more_input
;
78 if !p = 0 then raise e
;
81 let really_output o
s p l'
=
82 let sl = String.length
s in
83 if p + l'
> sl || p < 0 || l'
< 0 then invalid_arg
"IO.really_output";
87 let w = o
.out_output
s !p !l in
88 if w = 0 then raise Sys_blocked_io
;
95 let sl = String.length
s in
96 if p + l > sl || p < 0 || l < 0 then invalid_arg
"IO.input";
102 let really_input i
s p l'
=
103 let sl = String.length
s in
104 if p + l'
> sl || p < 0 || l'
< 0 then invalid_arg
"IO.really_input";
108 let r = i
.in_input
s !p !l in
109 if r = 0 then raise Sys_blocked_io
;
115 let really_nread i n
=
116 if n
< 0 then invalid_arg
"IO.really_nread";
119 let s = String.create n
121 ignore
(really_input i
s 0 n
);
125 let f _
= raise Input_closed
in
131 let write o x
= o
.out_write x
135 let l = ref (String.length
s) in
137 let w = o
.out_output
s !p !l in
138 if w = 0 then raise Sys_blocked_io
;
144 let sl = String.length
s in
145 if p + l > sl || p < 0 || l < 0 then invalid_arg
"IO.output";
149 Printf.kprintf
(fun s -> nwrite o
s) fmt
151 let flush o
= o
.out_flush
()
154 let f _
= raise Output_closed
in
155 let r = o
.out_close
() in
167 let s = nread i
maxlen in
168 str := (s,!pos) :: !str;
169 pos := !pos + String.length
s;
176 let buf = String.create
!pos in
177 List.iter
(fun (s,p) ->
178 String.unsafe_blit
s 0 buf p (String.length
s)
186 let c = i
.in_read
() in
190 in_input
= (fun s sp
l ->
191 let n = i
.in_input
s sp
l in
195 in_close
= i
.in_close
201 out_write
= (fun c ->
205 out_output
= (fun s sp
l ->
206 let n = o
.out_output
s sp
l in
210 out_close
= o
.out_close
;
211 out_flush
= o
.out_flush
;
214 (* -------------------------------------------------------------- *)
219 let len = String.length
s in
222 if !pos >= len then raise No_more_input
;
223 let c = String.unsafe_get
s !pos in
227 in_input
= (fun sout
p l ->
228 if !pos >= len then raise No_more_input
;
229 let n = (if !pos + l > len then len - !pos else l) in
230 String.unsafe_blit
s !pos sout
p n;
234 in_close
= (fun () -> ());
237 let output_string() =
238 let b = Buffer.create
0 in
240 out_write
= (fun c ->
243 out_output
= (fun s p l ->
244 Buffer.add_substring
b s p l;
247 out_close
= (fun () -> Buffer.contents
b);
248 out_flush
= (fun () -> ());
251 let input_channel ch
=
257 End_of_file
-> raise No_more_input
259 in_input
= (fun s p l ->
260 let n = Pervasives.input ch
s p l in
261 if n = 0 then raise No_more_input
;
264 in_close
= (fun () -> Pervasives.close_in ch
);
267 let output_channel ch
=
269 out_write
= (fun c -> output_char ch
c);
270 out_output
= (fun s p l -> Pervasives.output ch
s p l; l);
271 out_close
= (fun () -> Pervasives.close_out ch
);
272 out_flush
= (fun () -> Pervasives.flush ch
);
280 match Enum.get e with
281 | None -> raise No_more_input
286 in_input = (fun s p l ->
291 match Enum.get e with
294 String.unsafe_set s p c;
298 if k = l then raise No_more_input;
301 in_close = (fun () -> ());
305 let b = Buffer.create 0 in
307 out_write = (fun x ->
310 out_output = (fun s p l ->
311 Buffer.add_substring b s p l;
314 out_close = (fun () ->
315 let s = Buffer.contents b in
316 ExtString.String.enum s
318 out_flush = (fun () -> ());
323 let input = ref "" in
325 let output = Buffer.create
0 in
327 input := Buffer.contents
output;
330 if String.length
!input = 0 then raise No_more_input
333 if !inpos = String.length
!input then flush();
334 let c = String.unsafe_get
!input !inpos in
339 if !inpos = String.length
!input then flush();
340 let r = (if !inpos + l > String.length
!input then String.length
!input - !inpos else l) in
341 String.unsafe_blit
!input !inpos s p r;
346 Buffer.add_char
output c
349 Buffer.add_substring
output s p l;
355 in_close
= (fun () -> ());
360 out_close
= (fun () -> ());
361 out_flush
= (fun () -> ());
365 external cast_output
: 'a
output -> unit output = "%identity"
367 (* -------------------------------------------------------------- *)
370 exception Overflow
of string
372 let read_byte i
= int_of_char
(i
.in_read
())
374 let read_signed_byte i
=
375 let c = int_of_char
(i
.in_read
()) in
376 if c land 128 <> 0 then
382 let b = Buffer.create
8 in
384 let c = i
.in_read
() in
385 if c <> '
\000'
then begin
394 let b = Buffer.create
8 in
395 let cr = ref false in
397 let c = i
.in_read
() in
406 Buffer.add_char
b '
\r'
;
418 if !cr then Buffer.add_char
b '
\r'
;
419 if Buffer.length
b > 0 then
425 let ch1 = read_byte i
in
426 let ch2 = read_byte i
in
430 let ch1 = read_byte i
in
431 let ch2 = read_byte i
in
432 let n = ch1 lor (ch2 lsl 8) in
433 if ch2 land 128 <> 0 then
439 let ch1 = read_byte ch
in
440 let ch2 = read_byte ch
in
441 let ch3 = read_byte ch
in
442 let ch4 = read_byte ch
in
443 if ch4 land 128 <> 0 then begin
444 if ch4 land 64 = 0 then raise
(Overflow
"read_i32");
445 ch1 lor (ch2 lsl 8) lor (ch3 lsl 16) lor ((ch4 land 127) lsl 24)
447 if ch4 land 64 <> 0 then raise
(Overflow
"read_i32");
448 ch1 lor (ch2 lsl 8) lor (ch3 lsl 16) lor (ch4 lsl 24)
451 let read_real_i32 ch
=
452 let ch1 = read_byte ch
in
453 let ch2 = read_byte ch
in
454 let ch3 = read_byte ch
in
455 let base = Int32.of_int
(ch1 lor (ch2 lsl 8) lor (ch3 lsl 16)) in
456 let big = Int32.shift_left
(Int32.of_int
(read_byte ch
)) 24 in
460 let ch1 = read_byte ch
in
461 let ch2 = read_byte ch
in
462 let ch3 = read_byte ch
in
463 let ch4 = read_byte ch
in
464 let base = Int64.of_int
(ch1 lor (ch2 lsl 8) lor (ch3 lsl 16)) in
465 let small = Int64.logor
base (Int64.shift_left
(Int64.of_int
ch4) 24) in
466 let big = Int64.of_int32
(read_real_i32 ch
) in
467 Int64.logor
(Int64.shift_left
big 32) small
470 Int64.float_of_bits
(read_i64 ch
)
473 (* doesn't test bounds of n in order to keep semantics of Pervasives.output_byte *)
474 write o
(Char.unsafe_chr
(n land 0xFF))
476 let write_string o
s =
484 let write_ui16 ch
n =
485 if n < 0 || n > 0xFFFF then raise
(Overflow
"write_ui16");
487 write_byte ch
(n lsr 8)
490 if n < -0x8000 || n > 0x7FFF then raise
(Overflow
"write_i16");
492 write_ui16 ch
(65536 + n)
498 write_byte ch
(n lsr 8);
499 write_byte ch
(n lsr 16);
500 write_byte ch
(n asr 24)
502 let write_real_i32 ch
n =
503 let base = Int32.to_int
n in
504 let big = Int32.to_int
(Int32.shift_right_logical
n 24) in
506 write_byte ch
(base lsr 8);
507 write_byte ch
(base lsr 16);
511 write_real_i32 ch
(Int64.to_int32
n);
512 write_real_i32 ch
(Int64.to_int32
(Int64.shift_right_logical
n 32))
514 let write_double ch
f =
515 write_i64 ch
(Int64.bits_of_float
f)
517 (* -------------------------------------------------------------- *)
520 module BigEndian
= struct
523 let ch2 = read_byte i
in
524 let ch1 = read_byte i
in
528 let ch2 = read_byte i
in
529 let ch1 = read_byte i
in
530 let n = ch1 lor (ch2 lsl 8) in
531 if ch2 land 128 <> 0 then
537 let ch4 = read_byte ch
in
538 let ch3 = read_byte ch
in
539 let ch2 = read_byte ch
in
540 let ch1 = read_byte ch
in
541 if ch4 land 128 <> 0 then begin
542 if ch4 land 64 = 0 then raise
(Overflow
"read_i32");
543 ch1 lor (ch2 lsl 8) lor (ch3 lsl 16) lor ((ch4 land 127) lsl 24)
545 if ch4 land 64 <> 0 then raise
(Overflow
"read_i32");
546 ch1 lor (ch2 lsl 8) lor (ch3 lsl 16) lor (ch4 lsl 24)
549 let read_real_i32 ch
=
550 let big = Int32.shift_left
(Int32.of_int
(read_byte ch
)) 24 in
551 let ch3 = read_byte ch
in
552 let ch2 = read_byte ch
in
553 let ch1 = read_byte ch
in
554 let base = Int32.of_int
(ch1 lor (ch2 lsl 8) lor (ch3 lsl 16)) in
558 let big = Int64.of_int32
(read_real_i32 ch
) in
559 let ch4 = read_byte ch
in
560 let ch3 = read_byte ch
in
561 let ch2 = read_byte ch
in
562 let ch1 = read_byte ch
in
563 let base = Int64.of_int
(ch1 lor (ch2 lsl 8) lor (ch3 lsl 16)) in
564 let small = Int64.logor
base (Int64.shift_left
(Int64.of_int
ch4) 24) in
565 Int64.logor
(Int64.shift_left
big 32) small
568 Int64.float_of_bits
(read_i64 ch
)
570 let write_ui16 ch
n =
571 if n < 0 || n > 0xFFFF then raise
(Overflow
"write_ui16");
572 write_byte ch
(n lsr 8);
576 if n < -0x8000 || n > 0x7FFF then raise
(Overflow
"write_i16");
578 write_ui16 ch
(65536 + n)
583 write_byte ch
(n asr 24);
584 write_byte ch
(n lsr 16);
585 write_byte ch
(n lsr 8);
588 let write_real_i32 ch
n =
589 let base = Int32.to_int
n in
590 let big = Int32.to_int
(Int32.shift_right_logical
n 24) in
592 write_byte ch
(base lsr 16);
593 write_byte ch
(base lsr 8);
597 write_real_i32 ch
(Int64.to_int32
(Int64.shift_right_logical
n 32));
598 write_real_i32 ch
(Int64.to_int32
n)
600 let write_double ch
f =
601 write_i64 ch
(Int64.bits_of_float
f)
605 (* -------------------------------------------------------------- *)
614 type in_bits
= input bc
615 type out_bits
= unit output bc
633 let rec read_bits b n =
634 if b.nbits
>= n then begin
635 let c = b.nbits
- n in
636 let k = (b.bits
asr c) land ((1 lsl n) - 1) in
640 let k = read_byte b.ch
in
641 if b.nbits
>= 24 then begin
642 if n >= 31 then raise Bits_error
;
643 let c = 8 + b.nbits
- n in
644 let d = b.bits
land ((1 lsl b.nbits
) - 1) in
645 let d = (d lsl (8 - c)) lor (k lsr c) in
650 b.bits
<- (b.bits
lsl 8) lor k;
651 b.nbits
<- b.nbits
+ 8;
659 let rec write_bits b ~nbits x
=
661 if n + b.nbits
>= 32 then begin
662 if n > 31 then raise Bits_error
;
663 let n2 = 32 - b.nbits
- 1 in
665 write_bits b ~nbits
:n2 (x
asr n3);
666 write_bits b ~nbits
:n3 (x
land ((1 lsl n3) - 1));
668 if n < 0 then raise Bits_error
;
669 if (x
< 0 || x
> (1 lsl n - 1)) && n <> 31 then raise Bits_error
;
670 b.bits
<- (b.bits
lsl n) lor x
;
671 b.nbits
<- b.nbits
+ n;
672 while b.nbits
>= 8 do
673 b.nbits
<- b.nbits
- 8;
674 write_byte b.ch
(b.bits
asr b.nbits
)
679 if b.nbits
> 0 then write_bits b (8 - b.nbits
) 0
681 (* -------------------------------------------------------------- *)
684 class in_channel ch
=
686 method input s pos len = input ch
s pos len
687 method close_in() = close_in ch
690 class out_channel ch
=
692 method output s pos len = output ch
s pos len
693 method flush() = flush ch
694 method close_out() = ignore
(close_out ch
)
699 method get
() = try read ch
with No_more_input
-> raise End_of_file
700 method close_in() = close_in ch
705 method put t
= write ch t
706 method flush() = flush ch
707 method close_out() = ignore
(close_out ch
)
710 let from_in_channel ch
=
711 let cbuf = String.create
1 in
714 if ch#
input cbuf 0 1 = 0 then raise Sys_blocked_io
;
715 String.unsafe_get
cbuf 0
717 End_of_file
-> raise No_more_input
727 let from_out_channel ch
=
728 let cbuf = String.create
1 in
730 String.unsafe_set
cbuf 0 c;
731 if ch#
output cbuf 0 1 = 0 then raise Sys_blocked_io
;
742 let from_in_chars ch
=
747 String.unsafe_set
s (p + !i) (ch#get
());
752 End_of_file
when !i > 0 ->
760 let from_out_chars ch
=
762 for i = p to p + l - 1 do
763 ch#put
(String.unsafe_get
s i)