123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641(*
* BatInnerIO - Abstract input/output (inner module)
* Copyright (C) 2003 Nicolas Cannasse
* 2008 Philippe Strauss
* 2008 David Teller
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version,
* with the special exception on linking described in file LICENSE.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*)##V>=5##modulePervasives=Stdlibtype'aweak_set=('a,unit)BatInnerWeaktbl.tletweak_createsize=BatInnerWeaktbl.createsizeletweak_addsetelement=BatInnerWeaktbl.addsetelement()letweak_iterfs=BatInnerWeaktbl.iter(funx_->fx)stypeinput={mutablein_read:unit->char;mutablein_input:Bytes.t->int->int->int;mutablein_close:unit->unit;in_id:int;(**A unique identifier.*)in_upstream:inputweak_set}type'aoutput={mutableout_write:char->unit;mutableout_output:Bytes.t->int->int->int;mutableout_close:unit->'a;mutableout_flush:unit->unit;out_id:int;(**A unique identifier.*)out_upstream:unitoutputweak_set(** The set of outputs which have been created to write to this output.*)}moduleInput=structtypet=inputletcomparexy=x.in_id-y.in_idlethashx=x.in_idletequalxy=x.in_id=y.in_idendmoduleOutput=structtypet=unitoutputletcomparexy=x.out_id-y.out_idlethashx=x.out_idletequalxy=x.out_id=y.out_idend(**All the currently opened outputs -- used to permit [flush_all] and [close_all].*)(*module Inputs = Weaktbl.Make(Input)*)moduleOutputs=Weak.Make(Output)(** {6 Primitive operations}*)externalnoop:unit->unit="%ignore"externalcast_output:'aoutput->unitoutput="%identity"letlock=refBatConcurrent.nolockletoutputs=Outputs.create32letoutputs_addout=BatConcurrent.sync!lock(Outputs.addoutputs)outletoutputs_removeout=BatConcurrent.sync!lock(Outputs.removeoutputs)outexceptionNo_more_inputexceptionInput_closedexceptionOutput_closedletpost_incrr=letresult=!rinincrr;resultletpostrop=letresult=!rinr:=op!r;resultletuid=ref0letuid()=post_incruidleton_close_outoutf=BatConcurrent.sync!lock(fun()->letdo_close=out.out_closeinout.out_close<-(fun()->fout;do_close()))()leton_close_ininpf=BatConcurrent.sync!lock(fun()->letdo_close=inp.in_closeininp.in_close<-(fun()->finp;do_close()))()letclose_ini=letf_=raiseInput_closedini.in_close();i.in_read<-f;i.in_input<-f;i.in_close<-noop(*Double closing is not a problem*)letwrap_in~read~input~close~underlying=letresult={in_read=read;in_input=input;in_close=close;in_id=uid();in_upstream=weak_create2}inBatConcurrent.sync!lock(List.iter(funx->weak_addx.in_upstreamresult))underlying;Gc.finaliseclose_inresult;resultletinherit_in?read?input?closeinp=letread=matchreadwithNone->inp.in_read|Somef->fandinput=matchinputwithNone->inp.in_input|Somef->fandclose=matchclosewithNone->ignore|Somef->finwrap_in~read~input~close~underlying:[inp]letcreate_in~read~input~close=wrap_in~read~input~close~underlying:[](*For recursively closing outputs, we need either polymorphic
recursion or a hack. Well, a hack it is.*)(*Close a [unit output] -- note that this works for any kind of output,
thanks to [cast_output], but this can't return a proper result.*)letrecclose_unit(o:unitoutput):unit=letforbidden_=raiseOutput_closedino.out_flush();weak_iterclose_unito.out_upstream;letr=o.out_close()ino.out_write<-forbidden;o.out_output<-forbidden;o.out_close<-(fun_->r)(*Closing again is not a problem*);o.out_flush<-noop(*Flushing again is not a problem*);()(*Close a ['a output] -- first close it as a [unit output] then
recover the result.*)letclose_outo=(* Printf.eprintf "close_out\n%!";*)close_unit(cast_outputo);o.out_close()letignore_close_outout=ignore(close_outout)letwrap_out~write~output~flush~close~underlying=letrecout={out_write=write;out_output=output;out_close=(fun()->outputs_remove(cast_outputout);close());out_flush=flush;out_id=uid();out_upstream=weak_create2}inleto=cast_outputoutinBatConcurrent.sync!lock(List.iter(funx->weak_addx.out_upstreamo))underlying;outputs_add(cast_outputout);Gc.finaliseignore_close_outout;outletinherit_out?write?output?flush?closeout=letwrite=matchwritewithNone->out.out_write|Somef->fandoutput=matchoutputwithNone->out.out_output|Somef->fandflush=matchflushwithNone->out.out_flush|Somef->fandclose=matchclosewithNone->ignore|Somef->finwrap_out~write~output~flush~close~underlying:[out]letcreate_out~write~output~flush~close=wrap_out~write~output~flush~close~underlying:[]letreadi=i.in_read()letnreadin=ifn<0theninvalid_arg"BatIO.nread";ifn=0then""elselets=Bytes.createninletl=refninletp=ref0intrywhile!l>0doletr=i.in_inputs!p!linifr=0thenraiseNo_more_input;p:=!p+r;l:=!l-r;done;Bytes.unsafe_to_stringswithNo_more_inputase->if!p=0thenraisee;Bytes.sub_strings0!pletreally_outputospl'=letsl=Bytes.lengthsinifp+l'>sl||p<0||l'<0theninvalid_arg"BatIO.really_output";letl=refl'inletp=refpinwhile!l>0doletw=o.out_outputs!p!linifw=0thenraiseSys_blocked_io;p:=!p+w;l:=!l-w;done;l'letreally_output_substringospl'=really_outputo(Bytes.of_strings)pl'letinputispl=letsl=Bytes.lengthsinifp+l>sl||p<0||l<0theninvalid_arg"BatIO.input";ifl=0then0elsei.in_inputsplletreally_inputispl'=letsl=Bytes.lengthsinifp+l'>sl||p<0||l'<0theninvalid_arg"BatIO.really_input";letl=refl'inletp=refpinwhile!l>0doletr=i.in_inputs!p!linifr=0thenraiseSys_blocked_io;p:=!p+r;l:=!l-r;done;l'letreally_nreadin=ifn<0theninvalid_arg"BatIO.really_nread";ifn=0then""elselets=Bytes.createninignore(really_inputis0n);Bytes.unsafe_to_stringsletwriteox=o.out_writexletnwrite_bytesos=letp=ref0inletl=ref(Bytes.lengths)inwhile!l>0doletw=o.out_outputs!p!lin(* FIXME: unknown how many characters were already written *)ifw=0thenraiseSys_blocked_io;p:=!p+w;l:=!l-w;doneletnwriteos=nwrite_byteso(Bytes.unsafe_of_strings)letoutputospl=letsl=Bytes.lengthsinifp+l>sl||p<0||l<0theninvalid_arg"BatIO.output";o.out_outputsplletoutput_substringospl=outputo(Bytes.unsafe_of_strings)plletflusho=o.out_flush()letflush_all()=BatConcurrent.sync!lock(Outputs.iter(funo->tryflushowith_->()))outputsletclose_all()=letouts=BatConcurrent.sync!lock(Outputs.fold(funoos->o::os)outputs)[]inList.iter(funo->tryclose_outowith_->())outsletread_alli=letmaxlen=1024inletstr=ref[]inletpos=ref0inletrecloop()=lets=nreadimaxleninstr:=(s,!pos)::!str;pos:=!pos+String.lengths;loop()intryloop()withNo_more_input|Input_closed->letbuf=Bytes.create!posinList.iter(fun(s,p)->Bytes.blit_strings0bufp(String.lengths))!str;Bytes.unsafe_to_stringbufletinput_strings=letpos=ref0inletlen=String.lengthsincreate_in~read:(fun()->if!pos>=lenthenraiseNo_more_inputelseString.unsafe_gets(post_incrpos))~input:(funsoutpl->if!pos>=lenthenraiseNo_more_input;letn=(if!pos+l>lenthenlen-!poselsel)inBytes.blit_strings(postpos((+)n))soutpn;n)~close:noop(**
{6 Standard BatIO}
*)letdefault_buffer_size=16(*Arbitrary number. If you replace it, just
don't put something too small, i.e. anything
smaller than 10 is probably a bad idea.*)letoutput_string()=letb=Buffer.createdefault_buffer_sizeincreate_out~write:(func->Buffer.add_charbc)~output:(funspl->BatBytesCompat.buffer_add_subbytesbspl;l)~close:(fun()->Buffer.contentsb)~flush:noop(** A placeholder used to allow recursive use of [self]
in an [input_channel]*)letplaceholder_in={in_read=(fun()->' ');in_input=(fun___->0);in_close=noop;in_id=(-1);in_upstream=weak_create0}letinput_channel?(autoclose=true)?(cleanup=true)ch=letme=refplaceholder_in(*placeholder*)inletresult=create_in~read:(fun()->tryinput_charchwithEnd_of_file->ifautoclosethenclose_in!me;raiseNo_more_input)~input:(funspl->letn=Pervasives.inputchsplinifn=0thenbeginifautoclosethenclose_in!meelse();raiseNo_more_inputendelsen)~close:(ifcleanupthenfun()->Pervasives.close_inchelseignore)inme:=result;resultletoutput_channel?(cleanup=false)ch=create_out~write:(func->output_charchc)~output:(funspl->Pervasives.outputchspl;l)~close:(ifcleanupthenfun()->begin(* Printf.eprintf "Cleaning up\n%!";*)Pervasives.close_outchendelsefun()->begin(* Printf.eprintf "Not cleaning up\n%!";*)Pervasives.flushchend)~flush:(fun()->Pervasives.flushch)letpipe()=letinput=ref""inletinpos=ref0inletoutput=Buffer.createdefault_buffer_sizeinletflush()=input:=Buffer.contentsoutput;inpos:=0;Buffer.resetoutput;ifString.length!input=0thenraiseNo_more_inputinletread()=if!inpos=String.length!inputthenflush();String.unsafe_get!input(post_incrinpos)inletinputspl=if!inpos=String.length!inputthenflush();letr=if!inpos+l<=String.length!inputthenlelseString.length!input-!inposinBytes.blit_string!input!inposspr;inpos:=!inpos+r;rinletwritec=Buffer.add_charoutputcinletoutputspl=BatBytesCompat.buffer_add_subbytesoutputspl;linletinput=create_in~read~input~close:noopandoutput=create_out~write~output~close:noop~flush:noopininput,output(*let to_input_channel inp =
let (fin, fout) = Unix.pipe () in
let outp = out_channel fout in
(*connect [inp] to [outp]*)
in_channel_of_descr fin*)(**
{6 Binary APIs}
*)exceptionOverflowofstringletread_bytei=int_of_char(i.in_read())letread_signed_bytei=letc=int_of_char(i.in_read())inifcland128<>0thenc-256elsecletread_stringi=letb=Buffer.create8inletrecloop()=letc=i.in_read()inifc<>'\000'thenbeginBuffer.add_charbc;loop();end;inloop();Buffer.contentsbletread_linei=letb=Buffer.create80inletcr=reffalseinletrecloop()=matchi.in_read()with|'\n'->()|'\r'when!cr->Buffer.add_charb'\r';loop()|'\r'->cr:=true;loop()|cwhen!cr->cr:=false;Buffer.add_charb'\r';Buffer.add_charbc;loop();|c->Buffer.add_charbc;loop()intryloop();Buffer.contentsbwithNo_more_input->if!crthenBuffer.add_charb'\r';ifBuffer.lengthb>0thenBuffer.contentsbelseraiseNo_more_input(*$= read_line & ~cmp:BatString.equal ~printer:String.quote
"abc" (read_line (BatIO.input_string "abc\ndef\n"))
"abc" (read_line (BatIO.input_string "abc\r\ndef\n"))
"abc\r" (read_line (BatIO.input_string "abc\r\r\ndef\n"))
"abc" (read_line (BatIO.input_string "abc"))
"abc\r" (read_line (BatIO.input_string "abc\r"))
"kldsjf\r\r\rasdfa" (read_line (BatIO.input_string "kldsjf\r\r\rasdfa\nsfdsagf\n"))
*)letread_ui16i=letch1=read_byteiinletch2=read_byteiinch1lor(ch2lsl8)letread_i16i=letch1=read_byteiinletch2=read_byteiinletn=ch1lor(ch2lsl8)inifch2land128<>0thenn-65536elsenletfix=lnot0x7FFFFFFF(* -:) *)letread_i32ch=letch1=read_bytechinletch2=read_bytechinletch3=read_bytechinletch4=read_bytechinifch4land128<>0thenbeginifch4land64=0thenraise(Overflow"read_i32");(ch1lor(ch2lsl8)lor(ch3lsl16)lor((ch4land127)lsl24))lorfix(* FIX HERE *)endelsebeginifch4land64<>0thenraise(Overflow"read_i32");ch1lor(ch2lsl8)lor(ch3lsl16)lor(ch4lsl24)endletread_real_i32ch=letch1=read_bytechinletch2=read_bytechinletch3=read_bytechinletbase=Int32.of_int(ch1lor(ch2lsl8)lor(ch3lsl16))inletbig=Int32.shift_left(Int32.of_int(read_bytech))24inInt32.logorbasebigletread_i64ch=letch1=read_bytechinletch2=read_bytechinletch3=read_bytechinletch4=read_bytechinletbase=Int64.of_int(ch1lor(ch2lsl8)lor(ch3lsl16))inletsmall=Int64.logorbase(Int64.shift_left(Int64.of_intch4)24)inletbig=Int64.of_int32(read_real_i32ch)inInt64.logor(Int64.shift_leftbig32)smallletread_doublech=Int64.float_of_bits(read_i64ch)letread_floatch=Int32.float_of_bits(read_real_i32ch)letwrite_byteon=(* doesn't test bounds of n in order to keep semantics of Pervasives.output_byte *)writeo(Char.unsafe_chr(nland0xFF))letwrite_stringos=nwriteos;writeo'\000'letwrite_bytesob=nwriteobletwrite_lineos=nwriteos;writeo'\n'letwrite_ui16chn=ifn<0||n>0xFFFFthenraise(Overflow"write_ui16");write_bytechn;write_bytech(nlsr8)letwrite_i16chn=ifn<-0x8000||n>0x7FFFthenraise(Overflow"write_i16");ifn<0thenwrite_ui16ch(65536+n)elsewrite_ui16chnletwrite_i32chn=write_bytechn;write_bytech(nlsr8);write_bytech(nlsr16);write_bytech(nasr24)letwrite_real_i32chn=letbase=Int32.to_intninletbig=Int32.to_int(Int32.shift_right_logicaln24)inwrite_bytechbase;write_bytech(baselsr8);write_bytech(baselsr16);write_bytechbigletwrite_i64chn=write_real_i32ch(Int64.to_int32n);write_real_i32ch(Int64.to_int32(Int64.shift_right_logicaln32))letwrite_doublechf=write_i64ch(Int64.bits_of_floatf)letwrite_floatchf=write_real_i32ch(Int32.bits_of_floatf)letstdin=input_channelPervasives.stdinletstdout=output_channelPervasives.stdoutletstderr=output_channelPervasives.stderrletstdnull=create_out~write:ignore~output:(fun__l->l)~flush:ignore~close:ignoreletget_outputout=out.out_outputletget_flushout=out.out_flushletget_output_idout=out.out_idletget_input_idinp=inp.in_id