123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829# 1 "struct.cppo.ml"openHdf5_raw(* This is an interface to HDF5 tables, which have memory representation as C arrays of
structs. [Mem.t] represents an array of structs and [Ptr.t] represents a pointer to an
element of such array. Both are implemented as custom blocks to support marshalling.
Since GC does not examine custom blocks, memory management of the tables is done using
reference counting. Both [Mem.t] and [Ptr.t] point to a C struct [Mem.T.t] which is
allocated outside of the OCaml heap and which contains the reference count. [Mem.T.t]
also contains [nmemb] and [size] which describe the size and the shape of the table.
Marshalling supports sharing. *)moduletypeS=sigvalfields:Field.tlistend(* Pointer outside of the OCaml heap. *)moduleExt=structtypet=privateint(* [badd t i] adds [2 * i] bytes offset to the pointer [t]. *)letbadd(t:t)i:t=Obj.magic(Obj.magict+i)(* [badd t i] subtracts [2 * i] bytes offset from the pointer [t]. *)letbsub(t:t)i:t=Obj.magic(Obj.magict-i)(* [get_float64 t boffset] returns a float stored [2 * boffset] bytes from [t]. *)letget_float64(t:t)boffset=Array.unsafe_get(Obj.magic(baddtboffset):floatarray)0(* [set_float64 t boffset v] sets a float [2 * boffset] bytes from [t]. *)letset_float64(t:t)boffsetv=Array.unsafe_set(Obj.magic(baddtboffset):floatarray)0vexternalcaml_string_get_64:t->int->int64="%caml_string_get64u"externalcaml_string_set_64:t->int->int64->unit="%caml_string_set64u"(* [get_int64 t boffset] returns an int64 stored [2 * boffset] bytes from [t]. *)letget_int64(t:t)boffset=caml_string_get_64(baddtboffset)0(* [set_int64 t boffset v] sets an int64 [2 * boffset] bytes from [t]. *)letset_int64(t:t)boffsetv=caml_string_set_64(baddtboffset)0v(* [get_int t boffset] returns an int stored [2 * boffset] bytes from [t]. *)letget_inttboffset=Int64.to_int(get_int64tboffset)(* [set_int t boffset v] sets an int [2 * boffset] bytes from [t]. *)letset_inttboffsetv=set_int64tboffset(Int64.of_intv)(* [get_string t boffset] returns a string stored [2 * boffset] bytes from [t]. *)letget_string(t:t)poslen=letrecindextcllen=ifl>=lenthenlenelseifString.unsafe_get(Obj.magict)l=cthenlelseindextc(l+1)leninlett=baddtposinletlen=indext'\000'0leninlets=Bytes.createleninBytes.unsafe_blit(Obj.magict)0s0len;Bytes.unsafe_to_strings(* [set_string t boffset v] sets a string [2 * boffset] bytes from [t]. *)letset_string(t:t)poslenv=lett=baddtposinletvlen=String.lengthvinletmlen=iflen<vlenthenlenelsevleninBytes.unsafe_blit(Bytes.unsafe_of_stringv)0(Obj.magict)0mlen;Bytes.unsafe_fill(Obj.magict)mlen(len-mlen)'\000'end(* See the explanation at the top. *)moduleMem=struct(* This is a C structure living outside of the OCaml heap. This is not an OCaml record
so nothing other than [Mem.t] and [Ptr.t] should keep a reference to it. It is
implemented this was to allow quick access to the fields, otherwise we would have to
use relatively slow C calls to read each field. *)moduleT=structtypet={refcount:int;data:Ext.t;capacity:int;(* The capacity of [data] *)mutablenmemb:int;(* The number of records in the table *)size:int;(* The length of a record *)}endtypet={ops:Ext.t;(* Custom operations field *)t:T.t;}externalcreate:int->int->t="hdf5_caml_struct_mem_create"externalof_t:T.t->t="hdf5_caml_struct_mem_of_mem"externalrealloc:t->int->unit="hdf5_caml_struct_mem_realloc"externalblit:src:t->src_pos:int->dst:t->dst_pos:int->len:int->unit="hdf5_caml_struct_mem_blit"letdatat:H5tb.Data.t=Obj.magict.t.dataendmodulePtr=struct(* A pointer into an array of C structs wrapped in [Mem.t]. *)typet={ops:Ext.t;(* Pointer to the [pos]-th element of the array. *)mutableptr:Ext.t;(* The underlying [Mem.t]. *)mutablemem:Mem.T.t;(* The index of the element of the array pointed by [ptr]. *)mutablepos:int;}externalcreate:Mem.t->int->t="hdf5_caml_struct_ptr_create"(* [unsafe_next t bsize] moves the pointer to the next element of the array provided
that the length of the struct is [2 * bsize] bytes. *)letunsafe_nexttbsize=t.ptr<-Ext.baddt.ptrbsize;t.pos<-t.pos+1(* [unsafe_prev t bsize] moves the pointer to the previous element of the array provided
that the length of the struct is [2 * bsize] bytes. *)letunsafe_prevtbsize=t.ptr<-Ext.bsubt.ptrbsize;t.pos<-t.pos-1(* [unsafe_move t pos bsize] moves the pointer to the [pos]-th element of the array
provided that the length of the struct is [2 * bsize] bytes. *)letunsafe_movetposbsize=t.ptr<-Ext.baddt.mem.data(pos*bsize);t.pos<-pos(* Moves the pointer to the appropriate place in the array when [t.mem.data] changes. *)letresettbsize=t.ptr<-Ext.baddt.mem.data(t.pos*bsize)(* Like [unsafe_next] but raises exception if the pointer is out of bounds. *)letnexttbsize=letpos=t.pos+1inifpos>=t.mem.nmembthenraise(Invalid_argument"index out of bounds")elsebegint.ptr<-Ext.baddt.ptrbsize;t.pos<-posend(* Like [unsafe_prev] but raises exception if the pointer is out of bounds. *)letprevtbsize=letpos=t.pos-1inifpos<0thenraise(Invalid_argument"index out of bounds")elsebegint.ptr<-Ext.bsubt.ptrbsize;t.pos<-posend(* Like [unsafe_move] but raises exception if the pointer is out of bounds. *)letmovetposbsize=ifpos<0||pos>=t.mem.nmembthenraise(Invalid_argument"index out of bounds")elsebegint.ptr<-Ext.baddt.mem.data(pos*bsize);t.pos<-posendletget_float64tbo=Ext.get_float64t.ptrboletset_float64tbov=Ext.set_float64t.ptrbovletget_int64tbo=Ext.get_int64t.ptrboletset_int64tbov=Ext.set_int64t.ptrbovletget_inttbo=Ext.get_intt.ptrboletset_inttbov=Ext.set_intt.ptrbovletget_stringtbolen=Ext.get_stringt.ptrbolenletset_stringtbolenv=Ext.set_stringt.ptrbolenvletseek_float64tbsizebfield~min~maxv=letmid=refmininletmin=refmininletmax=refmaxinletdata=Ext.baddt.mem.databfieldinwhile!max>!min+1domid:=(!min+!max)asr1;letv'=Ext.get_float64data(!mid*bsize)inifv'<vthenmin:=!midelsemax:=!middone;letv'=Ext.get_float64data(!max*bsize)inifv'<=vthen!maxelse!min(* [seek_float64 t bsize bfield v] seeks the last element of the array with the value of
the given field less or equal [v]. The field is [2 * bfield] bytes from the
beginning of the struct. The length of the struct is [2 * bsize] bytes. The array
elements are sorted increasingly by the given field. *)letseek_float64tbsizebfieldv=letlen=t.mem.nmembinletdata=Ext.baddt.mem.databfieldinletv'=Ext.get_float64t.ptrbfieldinletpos=t.posinletmin=refposinletmax=refposinletstep=ref1inifv'<vthenbeginwhile!max<len&&Ext.get_float64data(!max*bsize)<vdomax:=!max+!step;step:=!step*2done;if!max>=lenthenmax:=len-1endelseifv'>vthenbeginwhile!min>0&&Ext.get_float64data(!min*bsize)>vdomin:=!min-!step;step:=!step*2done;if!min<0thenmin:=0end;unsafe_movet(if!max>!minthenseek_float64tbsizebfield~min:!min~max:!maxvelse!max)bsizeletseek_inttbsizebfield~min~maxv=letmid=refmininletmin=refmininletmax=refmaxinletdata=Ext.baddt.mem.databfieldinwhile!max>!min+1domid:=(!min+!max)asr1;letv'=Ext.get_intdata(!mid*bsize)inifv'<vthenmin:=!midelsemax:=!middone;letv'=Ext.get_intdata(!max*bsize)inifv'<=vthen!maxelse!min(* [seek_int t bsize bfield v] seeks the last element of the array with the value of
the given field less or equal [v]. The field is [2 * bfield] bytes from the
beginning of the struct. The length of the struct is [2 * bsize] bytes. The array
elements are sorted increasingly by the given field. *)letseek_inttbsizebfieldv=letlen=t.mem.nmembinletdata=Ext.baddt.mem.databfieldinletv'=Ext.get_intt.ptrbfieldinletpos=t.posinletmin=refposinletmax=refposinletstep=ref1inifv'<vthenbeginwhile!max<len&&Ext.get_intdata(!max*bsize)<vdomax:=!max+!step;step:=!step*2done;if!max>=lenthenmax:=len-1endelseifv'>vthenbeginwhile!min>0&&Ext.get_intdata(!max*bsize)>vdomin:=!min-!step;step:=!step*2done;if!min<0thenmin:=0end;unsafe_movet(if!max>!minthenseek_inttbsizebfield~min:!min~max:!maxvelse!max)bsizeletseek_int64tbsizebfield~min~max(v:int64)=letmid=refmininletmin=refmininletmax=refmaxinletdata=Ext.baddt.mem.databfieldinwhile!max>!min+1domid:=(!min+!max)asr1;letv'=Ext.get_int64data(!mid*bsize)inifv'<vthenmin:=!midelsemax:=!middone;letv'=Ext.get_int64data(!max*bsize)inifv'<=vthen!maxelse!min(* [seek_int64 t bsize bfield v] seeks the last element of the array with the value of
the given field less or equal [v]. The field is [2 * bfield] bytes from the
beginning of the struct. The length of the struct is [2 * bsize] bytes. The array
elements are sorted increasingly by the given field. *)letseek_int64tbsizebfieldv=letlen=t.mem.nmembinletdata=Ext.baddt.mem.databfieldinletv'=Ext.get_int64t.ptrbfieldinletpos=t.posinletmin=refposinletmax=refposinletstep=ref1inifv'<vthenbeginwhile!max<len&&Ext.get_int64data(!max*bsize)<vdomax:=!max+!step;step:=!step*2done;if!max>=lenthenmax:=len-1endelseifv'>vthenbeginwhile!min>0&&Ext.get_int64data(!min*bsize)>vdomin:=!min-!step;step:=!step*2done;if!min<0thenmin:=0end;unsafe_movet(if!max>!minthenseek_int64tbsizebfield~min:!min~max:!maxvelse!max)bsizeletseek_stringtbsizebfieldlen~min~max(v:string)=letmid=refmininletmin=refmininletmax=refmaxinletdata=Ext.baddt.mem.databfieldinwhile!max>!min+1domid:=(!min+!max)asr1;letv'=Ext.get_stringdata(!mid*bsize)leninifv'<vthenmin:=!midelsemax:=!middone;letv'=Ext.get_stringdata(!max*bsize)leninifv'<=vthen!maxelse!min(* [seek_string t bsize bfield len v] seeks the last element of the array with the value
of the given field less or equal [v]. The field is [2 * bfield] bytes from the
beginning of the struct. The length of the struct is [2 * bsize] bytes. The array
elements are sorted increasingly by the given field. *)letseek_stringtbsizebfieldslenv=letlen=t.mem.nmembinletdata=Ext.baddt.mem.databfieldinletv'=Ext.get_stringt.ptrbfieldsleninletpos=t.posinletmin=refposinletmax=refposinletstep=ref1inifv'<vthenbeginwhile!max<len&&Ext.get_stringdata(!max*bsize)slen<vdomax:=!max+!step;step:=!step*2done;if!max>=lenthenmax:=len-1endelseifv'>vthenbeginwhile!min>0&&Ext.get_stringdata(!min*bsize)slen>vdomin:=!min-!step;step:=!step*2done;if!min<0thenmin:=0end;unsafe_movet(if!max>!minthenseek_stringtbsizebfieldslen~min:!min~max:!maxvelse!max)bsizeendmoduleMake(S:S)=structincludeS(* This is necessary to prevent external pointers from being top level values of the
module, which would prevent it from being marshaled *)letmemoizef=letmemo=refNoneinfun()->match!memowith|Somevalue->value|None->letvalue=f()inmemo:=Somevalue;valueletnfields=List.lengthS.fieldslettype_bsize=List.fold_left(funsfield->s+(Type.sizefield.Field.type_+7)/8*8/2)0S.fieldslettype_size=2*type_bsizeletfield_names=List.map(funfield->field.Field.name)S.fields|>Array.of_listletfield_offset=letoffset=ref0inList.map(funfield->letfield_offset=!offsetinoffset:=!offset+(Type.sizefield.Field.type_+7)/8*8;field_offset)S.fields|>Array.of_listletfield_types=memoize(fun()->List.map(funfield->matchfield.Field.type_with|Type.Int|Type.Int64->H5t.native_long|Type.Float64->H5t.native_double|Type.Stringl->lettype_=H5t.copyH5t.c_s1inH5t.set_sizetype_l;type_)S.fields|>Array.of_list)letfield_sizes=List.map(funfield->Type.sizefield.Field.type_)S.fields|>Array.of_listletcompound_type=memoize(fun()->letdatatype=H5t.createH5t.Class.COMPOUNDtype_sizeinletfield_types=field_types()infori=0tonfields-1doH5t.insertdatatypefield_names.(i)field_offset.(i)field_types.(i)done;datatype)includePtrletpost=t.Ptr.poslethas_next(t:t)=t.pos+1<t.mem.nmemblethas_prevt=t.Ptr.pos>0letunsafe_nextt=Ptr.unsafe_nextttype_bsizeletnextt=Ptr.nextttype_bsizeletmoveti=Ptr.movetitype_bsizeletunsafe_moveti=Ptr.unsafe_movetitype_bsizemoduleArray=structtypee=ttypet=Mem.tletmakelen=Mem.createlentype_sizeletlength(t:t)=t.t.nmembletunsafe_get(t:t)pos=Ptr.createtposletinitlenf=iflen<0theninvalid_arg"Hdf5_caml.Struct.Array.init";lett=makeleniniflen=0thentelsebeginlete=unsafe_gett0infori=0tolen-2dofie;unsafe_nextedone;f(len-1)e;tendletget(t:t)pos=ifpos<0||pos>=t.t.nmembthenraise(Invalid_argument"index out of bounds");Ptr.createtposletmake_tablet?title?chunk_size?(compress=true)h5dset_name=lettitle=matchtitlewithSomet->t|None->dset_nameinletchunk_size=matchchunk_sizewith|Somes->s(* Chunk size must be <4GB *)|None->min(1024*1024)(max1(lengtht))inH5tb.make_tabletitle(H5.hidh5)(H5.escapedset_name)~nrecords:(lengtht)~type_size~field_names~field_offset~field_types:(field_types())~chunk_size~compress(Mem.datat)letappend_recordsth5dset_name=H5tb.append_records(H5.hidh5)(H5.escapedset_name)~nrecords:(lengtht)~type_size~field_offset~field_sizes(Mem.datat)letwrite_recordsth5~startdset_name=H5tb.write_records(H5.hidh5)(H5.escapedset_name)~start~nrecords:(lengtht)~type_size~field_offset~field_sizes(Mem.datat)letread_tableh5table_name=lettable_name=H5.escapetable_nameinletloc=H5.hidh5inletnrecords=H5tb.get_table_infoloctable_nameinlett=makenrecordsinH5tb.read_tableloctable_name~dst_size:type_size~dst_offset:field_offset~dst_sizes:field_sizes(Mem.datat);tletread_recordsh5~start~nrecordstable_name=letloc=H5.hidh5inlett=makenrecordsinH5tb.read_recordsloc(H5.escapetable_name)~start~nrecords~type_size~field_offset~dst_sizes:field_sizes(Mem.datat);tletwritet?(deflate=H5.default_deflate())h5name=letlen=lengthtinletdims=[|len|]inletdataspace=H5s.create_simpledimsin(* Chunk size must be <4GB *)dims.(0)<-min(1024*1024)dims.(0);letdcpl=matchdeflatewith|0->None|_whenlen=0->None|deflate->letdcpl=H5p.createH5p.Cls_id.DATASET_CREATEinH5p.set_chunkdcpldims;H5p.set_deflatedcpldeflate;Somedcplinletcompound_type=compound_type()inletdataset=H5d.create(H5.hidh5)(H5.escapename)compound_type?dcpldataspaceinH5d.write_stringdatasetcompound_typeH5s.allH5s.all(Mem.datat|>Obj.magic);H5d.closedataset;H5s.closedataspace;matchdcplwith|None->()|Somedcpl->H5p.closedcplletreadh5?dataname=lethid=H5.hidh5inletdataset=H5d.open_hid(H5.escapename)inletdatatype=H5d.get_typedatasetinletcompound_type=compound_type()inifnot(H5t.equalcompound_typedatatype)theninvalid_arg"Unexpected datatype";letdataspace=H5d.get_spacedatasetinletdims,_=H5s.get_simple_extent_dimsdataspaceinifArray.lengthdims<>1theninvalid_arg"Dataset not one dimensional";letdata=matchdatawith|Somedata->iflengthdata<dims.(0)theninvalid_arg"The provided data storage too small";data|None->makedims.(0)inH5d.read_stringdatasetdatatypeH5s.allH5s.all(Mem.datadata|>Obj.magic);H5s.closedataspace;H5t.closedatatype;H5d.closedataset;dataletitert~f=lete=unsafe_gett0infor_=0tolengtht-1dofe;unsafe_nextedoneletiterit~f=lete=unsafe_gett0infori=0tolengtht-1dofie;unsafe_nextedoneletdatat=Mem.datatendletcreate()=Array.unsafe_get(Array.make1)0letmemt=Mem.of_tt.memmoduleVector=structtypee=ttypet={mem:Mem.t;mutablegrowth_factor:float;mutableend_:e;mutableptrs:elist;mutableon_realloc:t->unit;}letcreate?(capacity=16)?(growth_factor=1.5)()=ifgrowth_factor<1.theninvalid_arg(Printf.sprintf"Invalid growth factor %f"growth_factor);letmem=Array.makecapacityinmem.t.nmemb<-0;{mem;growth_factor;end_=Array.unsafe_getmem(-1);ptrs=[];on_realloc=fun_->()}letcapacityt=t.mem.t.capacityletgrowth_factort=t.growth_factorletset_growth_factortgrowth_factor=ifgrowth_factor<=0.theninvalid_arg(Printf.sprintf"Given negative growth factor %g"growth_factor);t.growth_factor<-growth_factorletlengtht=t.mem.t.nmembletset_lengthtlength=t.mem.t.nmemb<-lengthletend_t=iflengtht<=0thenraise(Invalid_argument"index out of bounds");t.end_letrealloctcapacity=Mem.realloct.memcapacity;List.iter(funptr->Ptr.resetptrtype_bsize)t.ptrs;Ptr.resett.end_type_bsize;t.on_realloctletensure_capacitytc=ifc>capacitytthenrealloctcletappendt=letmem=t.mem.tinletnmemb=mem.nmembinletcapacity=mem.capacityinifcapacity=nmembthenrealloct(int_of_float(floatcapacity*.t.growth_factor)+1);mem.nmemb<-nmemb+1;nextt.end_;t.end_letcleart=set_lengtht0;unsafe_movet.end_(-1)letunsafe_getti=lete=Array.unsafe_gett.memiint.ptrs<-e::t.ptrs;eletgetti=ifi<0||i>=lengthtthenraise(Invalid_argument"index out of bounds");lete=Array.gett.memiint.ptrs<-e::t.ptrs;eletitert~f=letptr=t.end_inunsafe_moveptr0;letlen=lengthtinfor_=0tolen-1dofptr;unsafe_nextptrdone;unsafe_moveptr(len-1)letiterit~f=letptr=t.end_inunsafe_moveptr0;letlen=lengthtinfori=0tolen-1dofiptr;unsafe_nextptrdone;unsafe_moveptr(len-1)letof_array?(growth_factor=1.5)a=ifgrowth_factor<1.theninvalid_arg(Printf.sprintf"Invalid growth factor %f"growth_factor);letlen=Array.lengthain{mem=a;growth_factor;end_=Array.unsafe_geta(iflen>0thenlen-1else0);ptrs=[];on_realloc=fun_->()}letto_arrayt=letlen=lengthtinletmem=Array.makeleninMem.blit~src:t.mem~src_pos:0~dst:mem~dst_pos:0~len;memleton_realloctf=t.on_realloc<-fendmoduleQueue=structtypee=ttypet={mutablea:Array.t;mutablehd:e;mutabletl:e;mutablepeek:e;}letcreate?(capacity=16)()=ifcapacity<=0theninvalid_arg(Printf.sprintf"The given capacity %d cannot be negative"capacity);leta=Array.makecapacityin{a;hd=Array.geta0;tl=Array.geta0;peek=Array.geta0}letnextae=letcapacity=Array.lengthainletpos=poseinletnew_pos=pos+1inifnew_pos=capacitythenunsafe_movee0elseunsafe_nexteletis_emptyt=post.hd=post.tlletlengtht=letl=post.hd-post.tlinifl>=0thenlelsel+Array.lengtht.aletaddt=let{a;hd;tl;_}=tinnextahd;ifposhd<>postlthenhdelsebeginletcapacity=Array.lengthainletnew_capacity=1+capacity*3/2int.a<-Array.makenew_capacity;t.hd<-Array.gett.a0;t.tl<-Array.gett.a0;t.peek<-Array.gett.a0;letpos=poshdinifpos=0thenMem.blit~src:a~src_pos:0~dst:t.a~dst_pos:0~len:capacityelsebeginMem.blit~src:a~src_pos:pos~dst:t.a~dst_pos:0~len:(capacity-pos);Mem.blit~src:a~src_pos:0~dst:t.a~dst_pos:(capacity-pos)~len:posend;unsafe_movet.hdcapacity;t.hdendlettake{a;hd;tl;_}=ifposhd=postlthenraiseQueue.Empty;nextatl;tlletpeek{a;hd;tl;peek}=ifposhd=postlthenraiseQueue.Empty;unsafe_movepeek(postl);nextapeek;peekendendexternalreset_serialize:unit->unit="hdf5_caml_struct_reset_serialize"externalreset_deserialize:unit->unit="hdf5_caml_struct_reset_deserialize"let%test_module""=(modulestructmoduleFoo=structincludeMake(structletfields=[Field.create"id"Int;Field.create"name"(String10);]end)letidt=get_intt0letnamet=get_stringt410letsett~id~name=set_intt0id;set_stringt410nameendlet%test_unit_=letv=Foo.Vector.create()inlet_=Foo.Vector.appendvinletf=Foo.Vector.getv0infori=0to999dolets=string_of_intiinlete=Foo.Vector.appendvinFoo.sete~id:i~name:s;Foo.nextf;assert(Foo.idf=i);assert(Foo.namef=s);done;leta=Foo.Vector.to_arrayvinletf=Foo.Array.geta0inassert(not(Foo.has_prevf));assert(Foo.has_nextf);Foo.nextf;assert(Foo.has_prevf);assert(Foo.has_nextf);Foo.movef1000;assert(Foo.has_prevf);assert(not(Foo.has_nextf))let%test_unit_=letlen=32inletcreate_array()=Foo.Array.init(1+Random.intlen)(funie->Foo.sete~id:i~name:(string_of_inti))inleta=ref(Array.initlen(fun_->create_array()))inletcreate_element()=leta=!a.(Random.intlen)inletpos=Random.int(Foo.Array.lengtha)inpos,Foo.Array.getaposinlete=ref(Array.init(len*len)(fun_->create_element()))infor_=0tolen-1dofor_=0tolen-1doleta=!ainlete=!einfor_=0tolen-1doa.(Random.intlen)<-create_array();for_=0tolen-1doe.(Random.int(len*len))<-create_element();letpos,e=e.(Random.int(len*len))inassert(Foo.ide=pos);assert(Foo.namee=string_of_intpos)donedone;Gc.full_major()done;Gc.compact();reset_serialize();lets=Marshal.to_string(!a,!e)[]inreset_deserialize();leta',e'=Marshal.from_strings0ina:=a';e:=e'donelet%test_unit_=begintrylet_=Foo.Array.init(-1)(fun__->())inassertfalsewithInvalid_argument_->()endlet%test_unit_=leta=Foo.Array.init16(funie->Foo.sete~id:i~name:(string_of_inti))inreset_serialize();lets=Marshal.to_stringa[Closures]inreset_deserialize();letb=Marshal.from_strings0inassert(Foo.Array.lengtha=Foo.Array.lengthb);letf=Foo.Array.getb0inFoo.Array.iteria~f:(funie->Foo.movefi;assert(Foo.ide=Foo.idf);assert(Foo.namee=Foo.namef));lete=Foo.Array.geta8inreset_serialize();lets=Marshal.to_stringe[Closures]inreset_deserialize();lete=Marshal.from_strings0inassert(Foo.ide=8);assert(Foo.namee="8")end)