123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495(*
* Copyright (c) 2018 Romain Calascibetta <romain.calascibetta@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*)letdefault_alphabet="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"letio_buffer_size=65536letinvalid_argfmt=Format.ksprintf(funs->invalid_args)fmtletinvalid_boundsofflen=invalid_arg"Invalid bounds (off: %d, len: %d)"offlenletmalformedchr=`Malformed(String.make1chr)letunsafe_bytesourceoffpos=Bytes.unsafe_getsource(off+pos)letunsafe_blit=Bytes.unsafe_blitletunsafe_chr=Char.unsafe_chrletunsafe_set_chrsourceoffchr=Bytes.unsafe_setsourceoffchrtypestate={quantum:int;size:int;buffer:Bytes.t}letcontinuestate(quantum,size)=`Continue{statewithquantum;size}letflushstate=`Flush{statewithquantum=0;size=0}lettable="\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\062\255\255\255\063\052\053\054\055\056\057\058\059\060\061\255\255\255\255\255\255\255\000\001\002\003\004\005\006\007\008\009\010\011\012\013\014\015\016\017\018\019\020\021\022\023\024\025\255\255\255\255\255\255\026\027\028\029\030\031\032\033\034\035\036\037\038\039\040\041\042\043\044\045\046\047\048\049\050\051\255\255\255\255\255"letr_repr({quantum;size;_}asstate)chr=(* assert (0 <= off && 0 <= len && off + len <= String.length source); *)(* assert (len >= 1); *)letcode=Char.codetable.[Char.codechr]inmatchsizewith|0->continuestate(code,1)|1->continuestate((quantumlsl6)lorcode,2)|2->continuestate((quantumlsl6)lorcode,3)|3->unsafe_set_chrstate.buffer0(unsafe_chr((quantumlsr10)land255));unsafe_set_chrstate.buffer1(unsafe_chr((quantumlsr2)land255));unsafe_set_chrstate.buffer2(unsafe_chr((quantumlsl6)lorcodeland255));flushstate|_->malformedchrtypesrc=[`Channelofin_channel|`Stringofstring|`Manual]typedecode=[`Await|`End|`Wrong_padding|`Malformedofstring|`Flushofstring]typeinput=[`Line_break|`Wsp|`Padding|`Malformedofstring|`Flushofstate]typedecoder={src:src;mutablei:Bytes.t;mutablei_off:int;mutablei_pos:int;mutablei_len:int;mutables:state;mutablepadding:int;mutableunsafe:bool;mutablebyte_count:int;mutablelimit_count:int;mutablepp:decoder->input->decode;mutablek:decoder->decode;}leti_remdecoder=decoder.i_len-decoder.i_pos+1letend_of_inputdecoder=decoder.i<-Bytes.empty;decoder.i_off<-0;decoder.i_pos<-0;decoder.i_len<-min_intletsrcdecodersourceofflen=ifoff<0||len<0||off+len>Bytes.lengthsourcetheninvalid_boundsofflenelseiflen=0thenend_of_inputdecoderelse(decoder.i<-source;decoder.i_off<-off;decoder.i_pos<-0;decoder.i_len<-len-1)letrefillkdecoder=matchdecoder.srcwith|`Manual->decoder.k<-k;`Await|`String_->end_of_inputdecoder;kdecoder|`Channelic->letlen=inputicdecoder.i0(Bytes.lengthdecoder.i)insrcdecoderdecoder.i0len;kdecoderletdangerousdecoderv=decoder.unsafe<-vletresetdecoder=decoder.limit_count<-0letretkvbyte_countdecoder=decoder.k<-k;decoder.byte_count<-decoder.byte_count+byte_count;decoder.limit_count<-decoder.limit_count+byte_count;ifdecoder.limit_count>78thendangerousdecodertrue;decoder.ppdecodervtypeflush_and_malformed=[`Flushofstate|`Malformedofstring]letpadding{size;_}padding=match(size,padding)with|0,0->true|1,_->false|2,2->true|3,1->true|_->falselett_flush{quantum;size;buffer}=matchsizewith|0|1->`Flush{quantum;size;buffer=Bytes.empty}|2->letquantum=quantumlsr4in`Flush{quantum;size;buffer=Bytes.make1(unsafe_chr(quantumland255))}|3->letquantum=quantumlsr2inunsafe_set_chrbuffer0(unsafe_chr((quantumlsr8)land255));unsafe_set_chrbuffer1(unsafe_chr(quantumland255));`Flush{quantum;size;buffer=Bytes.subbuffer02}|_->assertfalse(* this branch is impossible, size can only ever be in the range [0..3]. *)letwrong_paddingdecoder=letk_=`Endindecoder.k<-k;`Wrong_paddingletrect_decode_base64chrdecoder=ifdecoder.padding=0thenletrecgopos=function|`Continuestate->ifdecoder.i_len-(decoder.i_pos+pos)+1>0then(matchunsafe_bytedecoder.idecoder.i_off(decoder.i_pos+pos)with|('A'..'Z'|'a'..'z'|'0'..'9'|'+'|'/')aschr->go(succpos)(r_reprstatechr)|'='->decoder.padding<-decoder.padding+1;decoder.i_pos<-decoder.i_pos+pos+1;decoder.s<-state;retdecode_base64`Padding(pos+1)decoder|' '|'\t'->decoder.i_pos<-decoder.i_pos+pos+1;decoder.s<-state;retdecode_base64`Wsp(pos+1)decoder|'\r'->decoder.i_pos<-decoder.i_pos+pos+1;decoder.s<-state;decode_base64_lf_after_crdecoder|chr->decoder.i_pos<-decoder.i_pos+pos+1;decoder.s<-state;retdecode_base64(malformedchr)(pos+1)decoder)else(decoder.i_pos<-decoder.i_pos+pos;decoder.byte_count<-decoder.byte_count+pos;decoder.limit_count<-decoder.limit_count+pos;decoder.s<-state;refilldecode_base64decoder)|#flush_and_malformedasv->decoder.i_pos<-decoder.i_pos+pos;retdecode_base64vposdecoderingo1(r_reprdecoder.schr)else(decoder.i_pos<-decoder.i_pos+1;retdecode_base64(malformedchr)1decoder)anddecode_base64_lf_after_crdecoder=letrem=i_remdecoderinifrem<0thenretdecode_base64(malformed'\r')1decoderelseifrem=0thenrefilldecode_base64_lf_after_crdecoderelsematchunsafe_bytedecoder.idecoder.i_offdecoder.i_poswith|'\n'->decoder.i_pos<-decoder.i_pos+1;retdecode_base64`Line_break2decoder|_->retdecode_base64(malformed'\r')1decoderanddecode_base64decoder=letrem=i_remdecoderinifrem<=0thenifrem<0thenret(fundecoder->ifpaddingdecoder.sdecoder.paddingthen`Endelsewrong_paddingdecoder)(t_flushdecoder.s)0decoderelserefilldecode_base64decoderelsematchunsafe_bytedecoder.idecoder.i_offdecoder.i_poswith|('A'..'Z'|'a'..'z'|'0'..'9'|'+'|'/')aschr->t_decode_base64chrdecoder|'='->decoder.padding<-decoder.padding+1;decoder.i_pos<-decoder.i_pos+1;retdecode_base64`Padding1decoder|' '|'\t'->decoder.i_pos<-decoder.i_pos+1;retdecode_base64`Wsp1decoder|'\r'->decoder.i_pos<-decoder.i_pos+1;decode_base64_lf_after_crdecoder|chr->decoder.i_pos<-decoder.i_pos+1;retdecode_base64(malformedchr)1decoderletpp_base64decoder=function|`Line_break->resetdecoder;decoder.kdecoder|`Wsp|`Padding->decoder.kdecoder|`Flushstate->decoder.s<-state;`Flush(Bytes.to_stringstate.buffer)|`Malformed_asv->vletdecodersrc=letpp=pp_base64inletk=decode_base64inleti,i_off,i_pos,i_len=matchsrcwith|`Manual->(Bytes.empty,0,1,0)|`Channel_->(Bytes.createio_buffer_size,0,1,0)|`Strings->(Bytes.unsafe_of_strings,0,0,String.lengths-1)in{src;i_off;i_pos;i_len;i;s={quantum=0;size=0;buffer=Bytes.create3};padding=0;unsafe=false;byte_count=0;limit_count=0;pp;k;}letdecodedecoder=decoder.kdecoderletdecoder_byte_countdecoder=decoder.byte_countletdecoder_srcdecoder=decoder.srcletdecoder_dangerousdecoder=decoder.unsafe(* / *)letinvalid_encode()=invalid_arg"Expected `Await encode"typedst=[`Channelofout_channel|`BufferofBuffer.t|`Manual]typeencode=[`Await|`End|`Charofchar]typeencoder={dst:dst;mutableo:Bytes.t;mutableo_off:int;mutableo_pos:int;mutableo_len:int;mutablec_col:int;i:Bytes.t;mutables:int;t:Bytes.t;mutablet_pos:int;mutablet_len:int;mutablek:encoder->encode->[`Ok|`Partial];}leto_remencoder=encoder.o_len-encoder.o_pos+1letdstencodersourceofflen=ifoff<0||len<0||off+len>Bytes.lengthsourcetheninvalid_boundsofflen;encoder.o<-source;encoder.o_off<-off;encoder.o_pos<-0;encoder.o_len<-len-1letdst_rem=o_remletpartialkencoder=function|`Await->kencoder|`Char_|`End->invalid_encode()letflushkencoder=matchencoder.dstwith|`Manual->encoder.k<-partialk;`Partial|`Channeloc->outputocencoder.oencoder.o_offencoder.o_pos;encoder.o_pos<-0;kencoder|`Bufferb->leto=Bytes.unsafe_to_stringencoder.oinBuffer.add_substringboencoder.o_offencoder.o_pos;encoder.o_pos<-0;kencoderlett_rangeencoderlen=encoder.t_pos<-0;encoder.t_len<-lenletrect_flushkencoder=letblitencoderlen=unsafe_blitencoder.tencoder.t_posencoder.oencoder.o_poslen;encoder.o_pos<-encoder.o_pos+len;encoder.t_pos<-encoder.t_pos+leninletrem=o_remencoderinletlen=encoder.t_len-encoder.t_pos+1inifrem<lenthen(blitencoderrem;flush(t_flushk)encoder)else(blitencoderlen;kencoder)letrecencode_line_breakkencoder=letrem=o_remencoderinlets,j,k=ifrem<2then(t_rangeencoder2;(encoder.t,0,t_flushk))elseletj=encoder.o_posinencoder.o_pos<-encoder.o_pos+2;(encoder.o,encoder.o_off+j,k)inunsafe_set_chrsj'\r';unsafe_set_chrs(j+1)'\n';encoder.c_col<-0;kencoderandencode_charchrk(encoder:encoder)=ifencoder.s>=2then(leta,b,c=(unsafe_byteencoder.i00,unsafe_byteencoder.i01,chr)inencoder.s<-0;letquantum=(Char.codealsl16)+(Char.codeblsl8)+Char.codecinleta=quantumlsr18inletb=(quantumlsr12)land63inletc=(quantumlsr6)land63inletd=quantumland63inletrem=o_remencoderinlets,j,k=ifrem<4then(t_rangeencoder4;(encoder.t,0,t_flush(k4)))elseletj=encoder.o_posinencoder.o_pos<-encoder.o_pos+4;(encoder.o,encoder.o_off+j,k4)inunsafe_set_chrsjdefault_alphabet.[a];unsafe_set_chrs(j+1)default_alphabet.[b];unsafe_set_chrs(j+2)default_alphabet.[c];unsafe_set_chrs(j+3)default_alphabet.[d];flushkencoder)else(unsafe_set_chrencoder.iencoder.schr;encoder.s<-encoder.s+1;k0encoder)andencode_trailingkencoder=matchencoder.swith|2->letb,c=(unsafe_byteencoder.i00,unsafe_byteencoder.i01)inencoder.s<-0;letquantum=(Char.codeblsl10)+(Char.codeclsl2)inletb=(quantumlsr12)land63inletc=(quantumlsr6)land63inletd=quantumland63inletrem=o_remencoderinlets,j,k=ifrem<4then(t_rangeencoder4;(encoder.t,0,t_flush(k4)))elseletj=encoder.o_posinencoder.o_pos<-encoder.o_pos+4;(encoder.o,encoder.o_off+j,k4)inunsafe_set_chrsjdefault_alphabet.[b];unsafe_set_chrs(j+1)default_alphabet.[c];unsafe_set_chrs(j+2)default_alphabet.[d];unsafe_set_chrs(j+3)'=';flushkencoder|1->letc=unsafe_byteencoder.i00inencoder.s<-0;letquantum=Char.codeclsl4inletc=(quantumlsr6)land63inletd=quantumland63inletrem=o_remencoderinlets,j,k=ifrem<4then(t_rangeencoder4;(encoder.t,0,t_flush(k4)))elseletj=encoder.o_posinencoder.o_pos<-encoder.o_pos+4;(encoder.o,encoder.o_off+j,k4)inunsafe_set_chrsjdefault_alphabet.[c];unsafe_set_chrs(j+1)default_alphabet.[d];unsafe_set_chrs(j+2)'=';unsafe_set_chrs(j+3)'=';flushkencoder|0->k0encoder|_->assertfalseandencode_base64encoderv=letkcol_countencoder=encoder.c_col<-encoder.c_col+col_count;encoder.k<-encode_base64;`Okinmatchvwith|`Await->k0encoder|`End->ifencoder.c_col=76thenencode_line_break(funencoder->encode_base64encoderv)encoderelseencode_trailingkencoder|`Charchr->letrem=o_remencoderinifrem<1thenflush(funencoder->encode_base64encoderv)encoderelseifencoder.c_col=76thenencode_line_break(funencoder->encode_base64encoderv)encoderelseencode_charchrkencoderletencoderdst=leto,o_off,o_pos,o_len=matchdstwith|`Manual->(Bytes.empty,1,0,0)|`Buffer_|`Channel_->(Bytes.createio_buffer_size,0,0,io_buffer_size-1)in{dst;o_off;o_pos;o_len;o;t=Bytes.create4;t_pos=1;t_len=0;c_col=0;i=Bytes.create3;s=0;k=encode_base64;}letencodeencoder=encoder.kencoderletencoder_dstencoder=encoder.dst