Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file decoder.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159typedecoder={buffer:Bytes.t;mutablepos:int;mutablemax:int}letppppf{buffer;pos;max;}=Fmt.pfppf"%S"(Bytes.sub_stringbufferpos(max-pos))letio_buffer_size=65536letdecoder()={buffer=Bytes.createio_buffer_size;pos=0;max=0}letdecoder_fromx=letmax=String.lengthxinletbuffer=Bytes.of_stringxin{buffer;pos=0;max;}typeerror=|End_of_input|Expected_charofchar|Unexpected_charofchar|Expected_stringofstring|Invalid_commandofstring|Expected_eol|Expected_eol_or_space|No_enough_space|Assert_predicateof(char->bool)|Invalid_codeofintletpp_errorppf=function|End_of_input->Fmt.stringppf"End_of_input"|Expected_charchr->Fmt.pfppf"(Expected_char %02x)"(Char.codechr)|Unexpected_charchr->Fmt.pfppf"(Unexpected_char %02x)"(Char.codechr)|Expected_strings->Fmt.pfppf"(Expected_string %s)"s|Invalid_commands->Fmt.pfppf"(Invalid_command %s)"s|Expected_eol->Fmt.stringppf"Expected_eol"|Expected_eol_or_space->Fmt.stringppf"Expected_eol_or_space"|No_enough_space->Fmt.stringppf"No_enough_space"|Assert_predicate_->Fmt.stringppf"(Assert_predicate #predicate)"|Invalid_codec->Fmt.pfppf"(Invalid_code %d)"ctype'vstate=|Okof'v|Readof{buffer:Bytes.t;off:int;len:int;continue:int->'vstate}|Errorofinfoandinfo={error:error;buffer:Bytes.t;committed:int}exceptionLeaveofinfoletreturn(typev)(v:v)_:vstate=Okvletsafekdecoder:'vstate=trykdecoderwithLeaveinfo->Errorinfoletend_of_inputdecoder=decoder.maxletpeek_chardecoder=ifdecoder.pos<end_of_inputdecoderthenSome(Bytes.unsafe_getdecoder.bufferdecoder.pos)elseNone(* XXX(dinosaure): in [angstrom] world, [peek_char] should try to read input
again. However, SMTP is a line-directed protocol where we can ensure to
have the full line at the top (with a queue) instead to have a
systematic check (which slow-down the process). *)letleave_with(decoder:decoder)error=raise(Leave{error;buffer=decoder.buffer;committed=decoder.pos;})letstringstrdecoder=letidx=ref0inletlen=String.lengthstrinwhiledecoder.pos+!idx<end_of_inputdecoder&&!idx<len&&Char.equal(Bytes.unsafe_getdecoder.buffer(decoder.pos+!idx))(String.unsafe_getstr!idx)doincridxdone;if!idx=lenthendecoder.pos<-decoder.pos+lenelseleave_withdecoder(Expected_stringstr)letjunk_chardecoder=ifdecoder.pos<end_of_inputdecoderthendecoder.pos<-decoder.pos+1elseleave_withdecoderEnd_of_inputletwhile1predicatedecoder=letidx=refdecoder.posinwhile!idx<end_of_inputdecoder&&predicate(Bytes.unsafe_getdecoder.buffer!idx)doincridxdone;if!idx-decoder.pos=0thenleave_withdecoder(Assert_predicatepredicate);letsub=decoder.buffer,decoder.pos,!idx-decoder.posin(* XXX(dinosaure): avoid sub-string operation. *)decoder.pos<-!idx;subletat_least_one_linedecoder=letpos=refdecoder.posinletchr=ref'\000'inlethas_cr=reffalseinwhile!pos<decoder.max&&(chr:=Bytes.unsafe_getdecoder.buffer!pos;not(!chr='\n'&&!has_cr))dohas_cr:=!chr='\r';incrposdone;!pos<decoder.max&&!chr='\n'&&!has_crletpromptkdecoder=ifdecoder.pos>0then(* XXX(dinosaure): compress *)(letrest=decoder.max-decoder.posinBytes.unsafe_blitdecoder.bufferdecoder.posdecoder.buffer0rest;decoder.max<-rest;decoder.pos<-0);letrecgooff=ifoff=Bytes.lengthdecoder.bufferthenError{error=No_enough_space;buffer=decoder.buffer;committed=decoder.pos;}elseifnot(at_least_one_line{decoderwithmax=off})(* XXX(dinosaure): we make a new decoder here and we did __not__ set [decoder.max] owned by end-user,
and this is exactly what we want. *)thenRead{buffer=decoder.buffer;off;len=Bytes.lengthdecoder.buffer-off;continue=(funlen->go(off+len))}else(decoder.max<-off;safekdecoder)ingodecoder.maxletpeek_while_eoldecoder=letidx=refdecoder.posinletchr=ref'\000'inlethas_cr=reffalseinwhile!idx<end_of_inputdecoder&&(chr:=Bytes.unsafe_getdecoder.buffer!idx;not(!chr=='\n'&&!has_cr))dohas_cr:=!chr=='\r';incridxdone;if!idx<end_of_inputdecoder&&!chr=='\n'&&!has_crthen(assert(!idx+1-decoder.pos>1);decoder.buffer,decoder.pos,!idx+1-decoder.pos)elseleave_withdecoderExpected_eolletpeek_while_eol_or_spacedecoder=letidx=refdecoder.posinletchr=ref'\000'inlethas_cr=reffalseinwhile!idx<end_of_inputdecoder&&(chr:=Bytes.unsafe_getdecoder.buffer!idx;not(!chr='\n'&&!has_cr)&&!chr<>' ')dohas_cr:=!chr='\r';incridxdone;if!idx<end_of_inputdecoder&&((!chr='\n'&&!has_cr)||(!chr=' '))then(decoder.buffer,decoder.pos,!idx+1-decoder.pos)elseleave_withdecoderExpected_eol_or_space