123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215openCoreopenS(* Parameterize over input type. *)moduleReader:BsonReader=structtypebson_type=|Doubleoffloat|Stringofstring|Document_start|Array_start|Binaryofbinary_type*bytes|ObjectIdofbytes|Booleanofbool|DateTimeofint64|Null|Regexof{pattern:string;options:string}(* Options must be stored in alphabetical order *)|JSCodeofstring|JSCode_with_scopeofstring|Int32ofint32|Timestampofint64|Int64ofint64|Decimal128ofbytes|Min_key|Max_key[@@derivingsexp]exceptionNo_dataofstringtyperead_result=|Fieldofstring*bson_type|End_of_document[@@derivingsexp]typet={read_bytes:int->bytes;read_char:unit->char}letread_int32d=letb=d.read_bytes4inletrechelperiacc=ifi<0thenaccelselethigh=Bytes.getbi|>Char.to_int|>Int32.of_int_exninletacc=Int32.(shift_leftacc8lorhigh)inhelper(i-1)accinhelper30lletread_int64d=letb=d.read_bytes8inletrechelperiacc=ifi<0thenaccelselethigh=Bytes.getbi|>Char.to_int|>Int64.of_intinletacc=Int64.(shift_leftacc8lorhigh)inhelper(i-1)accinhelper70Lletread_floatd=read_int64d|>Int64.float_of_bitsletread_stringd=letsize=read_int32dinletstr=d.read_bytes(Int32.to_int_exnsize)|>Bytes.to_stringinmatchd.read_char()with|'\x00'->str|c->failwithf"Malformed document: string terminated with %c instead of null character."c()letread_cstringd=letbuf=Buffer.create80inletrechelper()=letnext=d.read_char()inmatchnextwith|'\x00'->Buffer.contentsbuf|c->(Buffer.add_charbufc;helper())inhelper()letread_document_startd=let_size=read_int32dinDocument_startletread_array_startd=let_size=read_int32dinArray_startletread_boold=matchd.read_char()with|'\x00'->false|'\x01'->true|c->failwithf"Invalid value %c for boolean"c()letread_binaryd=letsize=read_int32d|>Int32.to_int_exninletbinary_type=matchd.read_char()with|'\x00'->Generic|'\x01'->Function|'\x02'->Binary_old|'\x03'->UUID_old|'\x04'->UUID|'\x05'->MD5|'\x06'->Encrypted|'\x80'->User_defined|x->failwithf"Invalid binary subtype %c"x()inletbin=d.read_bytessizeinBinary(binary_type,bin)letread_objectidd=d.read_bytes12letread_decimal128d=d.read_bytes16letread_regexd=letpattern=read_cstringdinletoptions=read_cstringdinRegex{pattern;options}letread_js_with_scoped=let_size=read_int32dinletcode=read_stringdinread_document_startd|>ignore;JSCode_with_scopecodeletread_nextd=matchd.read_char()with|'\x00'->End_of_document|c->trybeginletname=read_cstringdinmatchcwith|'\x01'->Field(name,Double(read_floatd))|'\x02'->Field(name,String(read_stringd))|'\x03'->Field(name,read_document_startd)|'\x04'->Field(name,read_array_startd)|'\x05'->Field(name,read_binaryd)|'\x07'->Field(name,ObjectId(read_objectidd))|'\x08'->Field(name,Boolean(read_boold))|'\x09'->Field(name,DateTime(read_int64d))|'\x0A'->Field(name,Null)|'\x0B'->Field(name,read_regexd)|'\x0D'->Field(name,JSCode(read_stringd))|'\x0F'->Field(name,read_js_with_scoped)|'\x10'->Field(name,Int32(read_int32d))|'\x11'->Field(name,Timestamp(read_int64d))|'\x12'->Field(name,Int64(read_int64d))|'\x13'->Field(name,Decimal128(read_decimal128d))|'\xFF'->Field(name,Min_key)|'\x7F'->Field(name,Max_key)|c->failwithf"TODO: name %s, code %d"name(Char.to_intc)()endwithx->(*sprintf "Unexpected exn at position %d" d.pos |> failwith*)raisexletof_bytes'b=letpos=ref0inletread_char()=if!pos>=Bytes.lengthbthenfailwithf"Reached end of file at position %d"!pos()elseleti=!posinletc=Bytes.getbiinpos:=i+1;cinletread_bytesn=letb=Bytes.subb~pos:!pos~len:ninpos:=!pos+n;bin{read_char;read_bytes}letof_bytesb=letreader=of_bytes'binlet_size=read_int32readerinreaderletof_strings=letbytes=Bytes.of_stringsinof_bytesbytesletof_in_channel'c=letread_bytesn=letb=Bytes.createninletbytes_read=Stdio.In_channel.inputc~buf:b~pos:0~len:ninifbytes_read=nthenbelseletpos=Stdio.In_channel.poscinNo_data(sprintf"No data at position %Ld"pos)|>raisein{read_char=(fun()->matchStdio.In_channel.input_charcwith|Somec->c|None->letpos=Stdio.In_channel.poscinNo_data(sprintf"No data at position %Ld"pos)|>raise);read_bytes=read_bytes}letof_in_channelc=letreader=of_in_channel'cinlet_size=read_int32readerinreaderend