123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414(*
Copyright (2010-2014) INCUBAID BVBA
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*)(* use Hotc for highlevel locked access *)moduleB=Bytesletnext_prefix(prefix:string):stringoption=letnext_charc=letcode=Char.codec+1inmatchcodewith|256->Char.chr0,true|code->Char.chrcode,falseinletrecinner(s:bytes)(pos:int):stringoption=letc,carry=next_char(B.getspos)inB.setsposc;matchcarry,poswith|false,_->Some(B.to_strings)|true,0->None|true,pos->inners(pos-1)inletcopy=B.of_stringprefixininnercopy((B.lengthcopy)-1)moduleS=Stringletprefix_match(prefix:string)(k:string)=letpl=S.lengthprefixinletrecoki=(i=pl)||(S.getprefixi=S.getki&&ok(i+1))inS.lengthk>=pl&&ok0moduleBdb=structtypebdb(* type stays abstract *)letoreader=1letowriter=2letocreat=4letotrunc=8letonolck=16letolcknb=32letotsync=64letdefault_mode=(oreaderlorowriterlorocreatlorolcknb)letreadonly_mode=(oreaderloronolck)typebdbcur(* type stays abstract *)externalfirst:bdb->bdbcur->unit="bdb_first"externalnext:bdb->bdbcur->unit="bdb_next"externalprev:bdb->bdbcur->unit="bdb_prev"externallast:bdb->bdbcur->unit="bdb_last"externalkey:bdb->bdbcur->string="bdb_key"externalvalue:bdb->bdbcur->string="bdb_value"externalkey3:bdb->bdbcur->string="bdb_key3"externalvalue3:bdb->bdbcur->string="bdb_value3"externalrecord:bdb->bdbcur->string*string="bdb_record"externaljump:bdb->bdbcur->string->unit="bdb_jump"letcurrent=0letbefore=1letafter=2externalcur_put:bdb->bdbcur->string->int->unit="bdb_cur_put"externalcur_out:bdb->bdbcur->unit="bdb_cur_out"externalout:bdb->string->unit="bdb_out"externalput:bdb->string->string->unit="bdb_put"externalget:bdb->string->string="bdb_get"externalget3:bdb->string->string="bdb_get3"externalget3_generic:bdb->string->int->int->string="bdb_get3_generic"externalget_nolock:bdb->string->string="bdb_get_nolock"externalputkeep:bdb->string->string->unit="bdb_putkeep"(* TODO: let getters return a "string option" isof throwing Not_found *)(* TODO: maybe loose the delete calls and hook it up in GC *)(* don't use these directly , use Hotc *)external_make:unit->bdb="bdb_make"external_delete:bdb->unit="bdb_delete"external_dbopen:bdb->string->int->unit="bdb_dbopen"external_dbclose:bdb->unit="bdb_dbclose"external_dbsync:bdb->unit="bdb_dbsync"external_dbsync_nolock:bdb->unit="bdb_dbsync_nolock"external_cur_make:bdb->bdbcur="bdb_cur_make"external_cur_delete:bdbcur->unit="bdb_cur_delete"external_tranbegin:bdb->unit="bdb_tranbegin"external_trancommit:bdb->unit="bdb_trancommit"external_tranabort:bdb->unit="bdb_tranabort"externalrange:bdb->stringoption->bool->stringoption->bool->int->stringarray="bdb_range_bytecode""bdb_range_native"externalprefix_keys:bdb->string->int->stringarray="bdb_prefix_keys"externalbdb_optimize:bdb->unit="bdb_optimize"external_bdb_defrag:bdb->int64->int="bdb_defrag"letdefrag?(step=Int64.max_int)bdb=_bdb_defragbdbstepexternalget_key_count:bdb->int64="bdb_key_count"externalsetcache:bdb->int->int->unit="bdb_setcache"typeopt=BDBTLARGEexternal_tune:bdb->(* int -> int -> int -> int -> int -> *)int->unit="bdb_tune"lettunebdbopts=letint_of_opt=functionBDBTLARGE->1lsl0inletint_of_opts=List.fold_left(funab->alorint_of_optb)0in_tunebdb(int_of_optsopts)letwith_cursorbdb(f:bdb->'a)=letcursor=_cur_makebdbintryletx=fbdbcursorinlet()=_cur_deletecursorinxwith|exn->let()=_cur_deletecursorinraiseexnletdelete_prefixbdb(prefix:string)=letcount=ref0inwith_cursorbdb(funbdbcur->trylet()=jumpbdbcurprefixinletrecstep()=letjumped_key=keybdbcurinifprefix_matchprefixjumped_keythenlet()=cur_outbdbcurin(* and jump to next *)let()=incrcountinstep()else()instep()with|Not_found->());!countletexistsbdbkey=trylet_=getbdbkeyintruewith|Not_found->falsetypedirection=|Ascending|Descendingtypeinclude_key=booltypestart_and_direction=|Keyofstring*include_key*direction|OmegaDescendingletrange'bdbstart_key_and_direction(accumulate:(string*string)->'a->('a*bool))(initial:'a):'a=letcursor_init,move_next=matchstart_key_and_directionwith|Key(start_key,include_key,dir)->beginmatchdirwith|Ascending->letskip_till_start_keybdbcur=tryjumpbdbcurstart_key;ifinclude_keythen()elsenextbdbcur;falsewithNot_found->true(* empty *)inskip_till_start_key,next|Descending->letinit_curbdbcur=tryjumpbdbcurstart_key;ifinclude_key&&keybdbcur=start_keythen()elseprevbdbcur;falsewithNot_found->lastbdbcur;falseininit_cur,prevend|OmegaDescending->(funbdbcur->lastbdbcur;false),previnwith_cursorbdb(funbdbcur->letisempty=cursor_initbdbcurinletrecloop(acc,continue)=ifnotcontinuethenaccelsebeginletrecord_=recordbdbcurinlet(acc',_)asres=accumulaterecord_accintrylet()=move_nextbdbcurinloopreswithNot_found->acc'endinifisemptytheninitialelseloop(initial,true))typeupper_border=|BKeyofstring*include_key|BOmegaletrange_ascendingbdb(first:string)finc(last_:upper_border)accumulateinitial=letcompkey=matchlast_with|BOmega->true|BKey(last_,linc)->beginmatchS.comparekeylast_with|0->linc|1->false|-1->true|_->failwith"impossible compare result"endinrange'bdb(Key(first,finc,Ascending))(fun((key,value)askv)acc->ifcompkeythenaccumulatekvaccelse(acc,false))initialletrange_descendingbdb(first:upper_border)(last_:string)lincaccumulateinitial=letcompkey=matchS.comparekeylast_with|0->linc|1->true|-1->false|_->failwith"impossible compare result"inrange'bdb(matchfirstwith|BOmega->OmegaDescending|BKey(k,inc)->Key(k,inc,Descending))(fun((key,value)askv)acc->ifcompkeythenaccumulatekvaccelse(acc,false))initialletrange_entries(prefix:string)bdbfirstfinclast_lincmax=letfirst=matchfirstwith|Somex->prefix^x|None->prefixinletlast_=matchlast_with|None->beginmatchnext_prefixprefixwith|None->BOmega|Somenprefix->BKey(nprefix,false)end|Somex->BKey((prefix^x),linc)inletpl=S.lengthprefixinlet_,result=range_ascendingbdbfirstfinclast_(fun(key,value)(count,result)->ifcount=maxthen((count,result),false)elseletl=S.lengthkeyinletkey2=S.subkeypl(l-pl)in(count+1,(key2,value)::result),true)(0,[])inArray.of_list(List.revresult)letrev_range_entriesprefixbdbfirstfinclast_lincmax=letpl=S.lengthprefixinlet_,result=range_descendingbdb(matchfirstwith|None->beginmatchnext_prefixprefixwith|None->BOmega|Somex->BKey(x,false)end|Somex->BKey(prefix^x,finc))(matchlast_with|None->prefix|Somex->prefix^x)linc(fun(key,value)(count,result)->ifcount=maxthen((count,result),false)elseletl=S.lengthkeyinletkey2=S.subkeypl(l-pl)in(count+1,(key2,value)::result),true)(0,[])inresultexternal_flags:bdb->int="bdb_flags"typeflag=BDBFOPEN|BDBFFATALletflagsbdb=letf=_flagsbdbinList.fold_left(funacc(s,c)->ifflandc<>0thens::accelseacc)[](* Shifts taken from tcbdb.h and tchdb.h *)[(BDBFOPEN,1lsl0);(BDBFFATAL,1lsl1)]external_copy_from_cursor:bdb->bdbcur->bdb->int->int="bdb_copy_from_cursor"letcopy_from_cursor~source~cursor~target~max=letcount=matchmaxwith|None->-1|Somei->iin_copy_from_cursorsourcecursortargetcount(* functions for standalone use of the Bdb module ------------------------ *)letcreate?(mode=default_mode)?(lcnum=1024)?(ncnum=512)filenameopts=letbdb=_make()insetcachebdblcnumncnum;tunebdbopts;_dbopenbdbfilenamemode;bdbletclosedb=_dbclosedbletdeletedb=_deletedbletsyncdb=_dbsyncdbletget_cursorbdb=_cur_makebdbend