123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222open!Import(** ['a Cheap_option.t] is like ['a option], but it doesn't box [some _] values.
There are several things that are unsafe about it:
- [float t array] (or any array-backed container) is not memory-safe
because float array optimization is incompatible with unboxed option
optimization. You have to use [Uniform_array.t] instead of [array].
- Nested options (['a t t]) don't work. They are believed to be
memory-safe, but not parametric.
- A record with [float t]s in it should be safe, but it's only [t] being
abstract that gives you safety. If the compiler was smart enough to peek
through the module signature then it could decide to construct a float
array instead. *)moduleCheap_option=struct(* This is taken from core. Rather than expose it in the public interface of base, just
keep a copy around here. *)letphys_same(typeab)(a:a)(b:b)=phys_equala(Caml.Obj.magicb:a)moduleT0:sigtype'atvalnone:_tvalsome:'a->'atvalis_none:_t->boolvalis_some:_t->boolvalvalue_exn:'at->'avalvalue_unsafe:'at->'avaliter_some:'at->f:('a->unit)->unitend=structtype+'at(* Being a pointer, no one outside this module can construct a value that is
[phys_same] as this one.
It would be simpler to use this value as [none], but we use an immediate instead
because it lets us avoid caml_modify when setting to [none], making certain
benchmarks significantly faster (e.g. ../bench/array_queue.exe).
this code is duplicated in Moption, and if we find yet another place where we want
it we should reconsider making it shared. *)letnone_substitute:_t=Caml.Obj.obj(Caml.Obj.new_blockCaml.Obj.abstract_tag1)letnone:_t=(* The number was produced by
[< /dev/urandom tr -c -d '1234567890abcdef' | head -c 16].
The idea is that a random number will have lower probability to collide with
anything than any number we can choose ourselves.
We are using a polymorphic variant instead of an integer constant because there
is a compiler bug where it wrongly assumes that the result of [if _ then c else
y] is not a pointer if [c] is an integer compile-time constant. This is being
fixed in https://github.com/ocaml/ocaml/pull/555. The "memory corruption" test
below demonstrates the issue. *)Caml.Obj.magic`x6e8ee3478e1d7449;;letis_nonex=phys_equalxnoneletis_somex=not(phys_equalxnone)letsome(typea)(x:a):at=ifphys_samexnonethennone_substituteelseCaml.Obj.magicx;;letvalue_unsafe(typea)(x:at):a=ifphys_equalxnone_substitutethenCaml.Obj.magicnoneelseCaml.Obj.magicx;;letvalue_exnx=ifis_somexthenvalue_unsafexelsefailwith"Option_array.get_some_exn: the element is [None]";;letiter_somet~f=ifis_sometthenf(value_unsafet)endmoduleT1=structincludeT0letof_option=function|None->none|Somex->somex;;let[@inline]to_optionx=ifis_somexthenSome(value_unsafex)elseNoneletto_sexpable=to_optionletof_sexpable=of_optionlett_sexp_grammar(typea)(grammar:aSexplib0.Sexp_grammar.t):atSexplib0.Sexp_grammar.t=Sexplib0.Sexp_grammar.coerce(Option.t_sexp_grammargrammar);;endincludeT1includeSexpable.Of_sexpable1(Option)(T1)endtype'at='aCheap_option.tUniform_array.t[@@deriving_inlinesexp,sexp_grammar]lett_of_sexp:'a.(Sexplib0.Sexp.t->'a)->Sexplib0.Sexp.t->'at=fun_of_a__001_x__003_->Uniform_array.t_of_sexp(Cheap_option.t_of_sexp_of_a__001_)x__003_;;letsexp_of_t:'a.('a->Sexplib0.Sexp.t)->'at->Sexplib0.Sexp.t=fun_of_a__004_x__005_->Uniform_array.sexp_of_t(Cheap_option.sexp_of_t_of_a__004_)x__005_;;let(t_sexp_grammar:'aSexplib0.Sexp_grammar.t->'atSexplib0.Sexp_grammar.t)=fun_'a_sexp_grammar->Uniform_array.t_sexp_grammar(Cheap_option.t_sexp_grammar_'a_sexp_grammar);;[@@@end]letempty=Uniform_array.emptyletcreate~len=Uniform_array.create~lenCheap_option.noneletinitn~f=Uniform_array.initn~f:(funi->Cheap_option.of_option(fi))letinit_somen~f=Uniform_array.initn~f:(funi->Cheap_option.some(fi))letlength=Uniform_array.lengthlet[@inline]getti=Cheap_option.to_option(Uniform_array.getti)letget_some_exnti=Cheap_option.value_exn(Uniform_array.getti)letis_noneti=Cheap_option.is_none(Uniform_array.getti)letis_someti=Cheap_option.is_some(Uniform_array.getti)letsettix=Uniform_array.setti(Cheap_option.of_optionx)letset_sometix=Uniform_array.setti(Cheap_option.somex)letset_noneti=Uniform_array.settiCheap_option.noneletswaptij=Uniform_array.swaptijletunsafe_getti=Cheap_option.to_option(Uniform_array.unsafe_getti)letunsafe_get_some_exnti=Cheap_option.value_exn(Uniform_array.unsafe_getti)letunsafe_get_some_assuming_someti=Cheap_option.value_unsafe(Uniform_array.unsafe_getti);;letunsafe_is_someti=Cheap_option.is_some(Uniform_array.unsafe_getti)letunsafe_settix=Uniform_array.unsafe_setti(Cheap_option.of_optionx)letunsafe_set_sometix=Uniform_array.unsafe_setti(Cheap_option.somex)letunsafe_set_noneti=Uniform_array.unsafe_settiCheap_option.noneletcleart=fori=0tolengtht-1dounsafe_set_nonetidone;;letiteriinput~f=fori=0tolengthinput-1dofi(unsafe_getinputi)done;;letiterinput~f=iteriinput~f:(fun(_:int)x->fx)letfoldiinput~init~f=letacc=refinitiniteriinput~f:(funielem->acc:=fi!accelem);!acc;;letfoldinput~init~f=foldiinput~init~f:(fun(_:int)accx->faccx)includeIndexed_container.Make_gen(structtypenonrec'at='attype'aelt='aoptionletfold=foldletfoldi=`Customfoldiletiter=`Customiterletiteri=`Customiteriletlength=`Customlengthend)letmapiinput~f=letoutput=create~len:(lengthinput)initeriinput~f:(funielem->unsafe_setoutputi(fielem));output;;letmapinput~f=mapiinput~f:(fun(_:int)elem->felem)letmap_someinput~f=letlen=lengthinputinletoutput=create~leninlet()=fori=0tolen-1doletopt=Uniform_array.unsafe_getinputiinCheap_option.iter_someopt~f:(funx->unsafe_set_someoutputi(fx))doneinoutput;;letof_arrayarray=init(Array.lengtharray)~f:(funi->Array.unsafe_getarrayi)letof_array_somearray=init_some(Array.lengtharray)~f:(funi->Array.unsafe_getarrayi);;letto_arrayt=Array.init(lengtht)~f:(funi->unsafe_getti)includeBlit.Make1_generic(structtypenonrec'at='atletlength=lengthletcreate_like~len_=create~lenletunsafe_blit=Uniform_array.unsafe_blitend)letcopy=Uniform_array.copymoduleFor_testing=structmoduleUnsafe_cheap_option=Cheap_optionend