patch #7498
[mldonkey.git] / src / utils / extlib / IO.ml
blob3b78d10fd3c7e779c829100f1868f0f68d8de6ce
1 (*
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
21 type input = {
22 mutable in_read : unit -> char;
23 mutable in_input : string -> int -> int -> int;
24 mutable in_close : unit -> unit;
27 type 'a output = {
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 (* -------------------------------------------------------------- *)
39 (* API *)
41 let default_close = (fun () -> ())
43 let create_in ~read ~input ~close =
45 in_read = read;
46 in_input = input;
47 in_close = close;
50 let create_out ~write ~output ~flush ~close =
52 out_write = write;
53 out_output = output;
54 out_close = close;
55 out_flush = flush;
58 let read i = i.in_read()
60 let nread i n =
61 if n < 0 then invalid_arg "IO.nread";
62 if n = 0 then
64 else
65 let s = String.create n in
66 let l = ref n in
67 let p = ref 0 in
68 try
69 while !l > 0 do
70 let r = i.in_input s !p !l in
71 if r = 0 then raise No_more_input;
72 p := !p + r;
73 l := !l - r;
74 done;
76 with
77 No_more_input as e ->
78 if !p = 0 then raise e;
79 String.sub s 0 !p
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";
84 let l = ref l' in
85 let p = ref p in
86 while !l > 0 do
87 let w = o.out_output s !p !l in
88 if w = 0 then raise Sys_blocked_io;
89 p := !p + w;
90 l := !l - w;
91 done;
94 let input i s p l =
95 let sl = String.length s in
96 if p + l > sl || p < 0 || l < 0 then invalid_arg "IO.input";
97 if l = 0 then
99 else
100 i.in_input s p l
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";
105 let l = ref l' in
106 let p = ref p in
107 while !l > 0 do
108 let r = i.in_input s !p !l in
109 if r = 0 then raise Sys_blocked_io;
110 p := !p + r;
111 l := !l - r;
112 done;
115 let really_nread i n =
116 if n < 0 then invalid_arg "IO.really_nread";
117 if n = 0 then ""
118 else
119 let s = String.create n
121 ignore(really_input i s 0 n);
124 let close_in i =
125 let f _ = raise Input_closed in
126 i.in_close();
127 i.in_read <- f;
128 i.in_input <- f;
129 i.in_close <- f
131 let write o x = o.out_write x
133 let nwrite o s =
134 let p = ref 0 in
135 let l = ref (String.length s) in
136 while !l > 0 do
137 let w = o.out_output s !p !l in
138 if w = 0 then raise Sys_blocked_io;
139 p := !p + w;
140 l := !l - w;
141 done
143 let output o s p l =
144 let sl = String.length s in
145 if p + l > sl || p < 0 || l < 0 then invalid_arg "IO.output";
146 o.out_output s p l
148 let printf o fmt =
149 Printf.kprintf (fun s -> nwrite o s) fmt
151 let flush o = o.out_flush()
153 let close_out o =
154 let f _ = raise Output_closed in
155 let r = o.out_close() in
156 o.out_write <- f;
157 o.out_output <- f;
158 o.out_close <- f;
159 o.out_flush <- f;
162 let read_all i =
163 let maxlen = 1024 in
164 let str = ref [] in
165 let pos = ref 0 in
166 let rec loop() =
167 let s = nread i maxlen in
168 str := (s,!pos) :: !str;
169 pos := !pos + String.length s;
170 loop()
173 loop()
174 with
175 No_more_input ->
176 let buf = String.create !pos in
177 List.iter (fun (s,p) ->
178 String.unsafe_blit s 0 buf p (String.length s)
179 ) !str;
182 let pos_in i =
183 let p = ref 0 in
185 in_read = (fun () ->
186 let c = i.in_read() in
187 incr p;
190 in_input = (fun s sp l ->
191 let n = i.in_input s sp l in
192 p := !p + n;
195 in_close = i.in_close
196 } , (fun () -> !p)
198 let pos_out o =
199 let p = ref 0 in
201 out_write = (fun c ->
202 o.out_write c;
203 incr p
205 out_output = (fun s sp l ->
206 let n = o.out_output s sp l in
207 p := !p + n;
210 out_close = o.out_close;
211 out_flush = o.out_flush;
212 } , (fun () -> !p)
214 (* -------------------------------------------------------------- *)
215 (* Standard IO *)
217 let input_string s =
218 let pos = ref 0 in
219 let len = String.length s in
221 in_read = (fun () ->
222 if !pos >= len then raise No_more_input;
223 let c = String.unsafe_get s !pos in
224 incr pos;
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;
231 pos := !pos + n;
234 in_close = (fun () -> ());
237 let output_string() =
238 let b = Buffer.create 0 in
240 out_write = (fun c ->
241 Buffer.add_char b 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 =
253 in_read = (fun () ->
255 input_char ch
256 with
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);
276 let input_enum e =
277 let pos = ref 0 in
279 in_read = (fun () ->
280 match Enum.get e with
281 | None -> raise No_more_input
282 | Some c ->
283 incr pos;
286 in_input = (fun s p l ->
287 let rec loop p l =
288 if l = 0 then
290 else
291 match Enum.get e with
292 | None -> l
293 | Some c ->
294 String.unsafe_set s p c;
295 loop (p + 1) (l - 1)
297 let k = loop p l in
298 if k = l then raise No_more_input;
299 l - k
301 in_close = (fun () -> ());
304 let output_enum() =
305 let b = Buffer.create 0 in
307 out_write = (fun x ->
308 Buffer.add_char b 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 () -> ());
322 let pipe() =
323 let input = ref "" in
324 let inpos = ref 0 in
325 let output = Buffer.create 0 in
326 let flush() =
327 input := Buffer.contents output;
328 inpos := 0;
329 Buffer.reset output;
330 if String.length !input = 0 then raise No_more_input
332 let read() =
333 if !inpos = String.length !input then flush();
334 let c = String.unsafe_get !input !inpos in
335 incr inpos;
338 let input s p l =
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;
342 inpos := !inpos + r;
345 let write c =
346 Buffer.add_char output c
348 let output s p l =
349 Buffer.add_substring output s p l;
352 let input = {
353 in_read = read;
354 in_input = input;
355 in_close = (fun () -> ());
356 } in
357 let output = {
358 out_write = write;
359 out_output = output;
360 out_close = (fun () -> ());
361 out_flush = (fun () -> ());
362 } in
363 input , output
365 external cast_output : 'a output -> unit output = "%identity"
367 (* -------------------------------------------------------------- *)
368 (* BINARY APIs *)
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
377 c - 256
378 else
381 let read_string i =
382 let b = Buffer.create 8 in
383 let rec loop() =
384 let c = i.in_read() in
385 if c <> '\000' then begin
386 Buffer.add_char b c;
387 loop();
388 end;
390 loop();
391 Buffer.contents b
393 let read_line i =
394 let b = Buffer.create 8 in
395 let cr = ref false in
396 let rec loop() =
397 let c = i.in_read() in
398 match c with
399 | '\n' ->
401 | '\r' ->
402 cr := true;
403 loop()
404 | _ when !cr ->
405 cr := false;
406 Buffer.add_char b '\r';
407 Buffer.add_char b c;
408 loop();
409 | _ ->
410 Buffer.add_char b c;
411 loop();
414 loop();
415 Buffer.contents b
416 with
417 No_more_input ->
418 if !cr then Buffer.add_char b '\r';
419 if Buffer.length b > 0 then
420 Buffer.contents b
421 else
422 raise No_more_input
424 let read_ui16 i =
425 let ch1 = read_byte i in
426 let ch2 = read_byte i in
427 ch1 lor (ch2 lsl 8)
429 let read_i16 i =
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
434 n - 65536
435 else
438 let read_i32 ch =
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)
446 end else begin
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
457 Int32.logor base big
459 let read_i64 ch =
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
469 let read_double ch =
470 Int64.float_of_bits (read_i64 ch)
472 let write_byte o n =
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 =
477 nwrite o s;
478 write o '\000'
480 let write_line o s =
481 nwrite o s;
482 write o '\n'
484 let write_ui16 ch n =
485 if n < 0 || n > 0xFFFF then raise (Overflow "write_ui16");
486 write_byte ch n;
487 write_byte ch (n lsr 8)
489 let write_i16 ch n =
490 if n < -0x8000 || n > 0x7FFF then raise (Overflow "write_i16");
491 if n < 0 then
492 write_ui16 ch (65536 + n)
493 else
494 write_ui16 ch n
496 let write_i32 ch n =
497 write_byte ch 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
505 write_byte ch base;
506 write_byte ch (base lsr 8);
507 write_byte ch (base lsr 16);
508 write_byte ch big
510 let write_i64 ch n =
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 (* -------------------------------------------------------------- *)
518 (* Big Endians *)
520 module BigEndian = struct
522 let read_ui16 i =
523 let ch2 = read_byte i in
524 let ch1 = read_byte i in
525 ch1 lor (ch2 lsl 8)
527 let read_i16 i =
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
532 n - 65536
533 else
536 let read_i32 ch =
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)
544 end else begin
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
555 Int32.logor base big
557 let read_i64 ch =
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
567 let read_double ch =
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);
573 write_byte ch n
575 let write_i16 ch n =
576 if n < -0x8000 || n > 0x7FFF then raise (Overflow "write_i16");
577 if n < 0 then
578 write_ui16 ch (65536 + n)
579 else
580 write_ui16 ch n
582 let write_i32 ch n =
583 write_byte ch (n asr 24);
584 write_byte ch (n lsr 16);
585 write_byte ch (n lsr 8);
586 write_byte ch n
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
591 write_byte ch big;
592 write_byte ch (base lsr 16);
593 write_byte ch (base lsr 8);
594 write_byte ch base
596 let write_i64 ch n =
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 (* -------------------------------------------------------------- *)
606 (* Bits API *)
608 type 'a bc = {
609 ch : 'a;
610 mutable nbits : int;
611 mutable bits : int;
614 type in_bits = input bc
615 type out_bits = unit output bc
617 exception Bits_error
619 let input_bits ch =
621 ch = ch;
622 nbits = 0;
623 bits = 0;
626 let output_bits ch =
628 ch = cast_output ch;
629 nbits = 0;
630 bits = 0;
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
637 b.nbits <- c;
639 end else begin
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
646 b.bits <- k;
647 b.nbits <- c;
649 end else begin
650 b.bits <- (b.bits lsl 8) lor k;
651 b.nbits <- b.nbits + 8;
652 read_bits b n;
656 let drop_bits b =
657 b.nbits <- 0
659 let rec write_bits b ~nbits x =
660 let n = nbits in
661 if n + b.nbits >= 32 then begin
662 if n > 31 then raise Bits_error;
663 let n2 = 32 - b.nbits - 1 in
664 let n3 = n - n2 in
665 write_bits b ~nbits:n2 (x asr n3);
666 write_bits b ~nbits:n3 (x land ((1 lsl n3) - 1));
667 end else begin
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)
675 done
678 let flush_bits b =
679 if b.nbits > 0 then write_bits b (8 - b.nbits) 0
681 (* -------------------------------------------------------------- *)
682 (* Generic IO *)
684 class in_channel ch =
685 object
686 method input s pos len = input ch s pos len
687 method close_in() = close_in ch
690 class out_channel ch =
691 object
692 method output s pos len = output ch s pos len
693 method flush() = flush ch
694 method close_out() = ignore(close_out ch)
697 class in_chars ch =
698 object
699 method get() = try read ch with No_more_input -> raise End_of_file
700 method close_in() = close_in ch
703 class out_chars ch =
704 object
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
712 let read() =
714 if ch#input cbuf 0 1 = 0 then raise Sys_blocked_io;
715 String.unsafe_get cbuf 0
716 with
717 End_of_file -> raise No_more_input
719 let input s p l =
720 ch#input s p l
722 create_in
723 ~read
724 ~input
725 ~close:ch#close_in
727 let from_out_channel ch =
728 let cbuf = String.create 1 in
729 let write c =
730 String.unsafe_set cbuf 0 c;
731 if ch#output cbuf 0 1 = 0 then raise Sys_blocked_io;
733 let output s p l =
734 ch#output s p l
736 create_out
737 ~write
738 ~output
739 ~flush:ch#flush
740 ~close:ch#close_out
742 let from_in_chars ch =
743 let input s p l =
744 let i = ref 0 in
746 while !i < l do
747 String.unsafe_set s (p + !i) (ch#get());
748 incr i
749 done;
751 with
752 End_of_file when !i > 0 ->
755 create_in
756 ~read:ch#get
757 ~input
758 ~close:ch#close_in
760 let from_out_chars ch =
761 let output s p l =
762 for i = p to p + l - 1 do
763 ch#put (String.unsafe_get s i)
764 done;
767 create_out
768 ~write:ch#put
769 ~output
770 ~flush:ch#flush
771 ~close:ch#close_out