123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134openCommonopenOassocopenOassocbopenOsetb(* Take care that must often redefine all function in the original
* oassoc.ml because if some methods are not redefined, for instance
* #clear, then if do wrapper over a oassocdbm, then even if oassocdbm
* redefine #clear, it will not be called, but instead the default
* method will be called that internally will call another method.
* So better delegate all the methods and override even the method
* with a default definition.
*
* In the same way sometimes an exn can occur at weird time. When
* we add an element, sometimes this may raise an exn such as Out_of_memory,
* but as we dont add directly but only at flush time, the exn
* may happen far later the user added something in this oassoc.
* Also in the case of Out_of_memory, even if the entry is not
* added in the wrapped, it will still be present in the cache
* and so the next flush will still generate an exn that again
* may not be cached. So for the moment if Out_of_memory then
* do something special and erase the entry in the cache.
*
* Cf also oassoc_cache.ml which can be even more efficient.
*)(* !!take care!!: this class has side effect, not a pure oassoc *)(* can not make it pure, cos the assoc have side effect on the cache *)class['a,'b]oassoc_buffermaxcached=object(o)inherit['a,'b]oassocvalcounter=ref0valcache=ref(newoassocb[])valdirty=ref(newosetbSet_.empty)valwrapped=refcachedmethodprivatemyflush=lethas_a_raised=reffalsein!dirty#iter(funk->trywrapped:=!wrapped#add(k,!cache#assock)withOut_of_memory->pr2"PBBBBBB: Out_of_memory in oassoc_buffer, but still empty cache";has_a_raised:=true;);dirty:=(newosetbSet_.empty);cache:=(newoassocb[]);counter:=0;if!has_a_raisedthenraiseOut_of_memorymethodmisc_op_hook2=o#myflushmethodempty=raiseTodo(* what happens in k is already present ? or if add multiple times
* the same k ? cache is a oassocb and so the previous binding is
* still there, but dirty is a set, and in myflush we iter based
* on dirty so we will flush only the last 'k' in the cache.
*)methodadd(k,v)=cache:=!cache#add(k,v);dirty:=!dirty#addk;incrcounter;if!counter>maxtheno#myflush;omethoditerf=o#myflush;(* bugfix: have to flush !!! *)!wrapped#iterfmethodkeys=o#myflush;(* bugfix: have to flush !!! *)!wrapped#keysmethodclear=o#myflush;(* bugfix: have to flush !!! *)!wrapped#clearmethodlength=o#myflush;!wrapped#lengthmethodview=raiseTodomethoddel(k,v)=cache:=!cache#del(k,v);(* TODO as for delkey, do a try over wrapped *)wrapped:=!wrapped#del(k,v);dirty:=!dirty#delk;omethodmeme=raiseTodomethodnull=raiseTodomethodassock=try!cache#assockwithNot_found->(* may launch Not_found, but this time, dont catch it *)letv=!wrapped#assockinbegincache:=!cache#add(k,v);(* otherwise can use too much mem *)incrcounter;if!counter>maxtheno#myflush;vendmethoddelkeyk=cache:=!cache#delkeyk;(* sometimes have not yet flushed, so may not be yet in, (could
* also flush in place of doing try).
*
* TODO would be better to see if was in cache (in case mean that
* perhaps not flushed and do try and in other case just cos del
* (without try) cos forcement flushed ou was an error *)begintrywrapped:=!wrapped#delkeykwithNot_found->()end;dirty:=!dirty#delk;oend