123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151open!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_kernel. 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->'aend=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]";;endmoduleT1=structincludeT0letof_option=function|None->none|Somex->somex;;letto_optionx=ifis_somexthenSome(value_unsafex)elseNoneletto_sexpable=to_optionletof_sexpable=of_optionendincludeT1includeSexpable.Of_sexpable1(Option)(T1)endtype'at='aCheap_option.tUniform_array.t[@@deriving_inlinesexp]lett_of_sexp:'a.(Ppx_sexp_conv_lib.Sexp.t->'a)->Ppx_sexp_conv_lib.Sexp.t->'at=let_tp_loc="src/option_array.ml.t"infun_of_a->funt->Uniform_array.t_of_sexp(Cheap_option.t_of_sexp_of_a)tletsexp_of_t:'a.('a->Ppx_sexp_conv_lib.Sexp.t)->'at->Ppx_sexp_conv_lib.Sexp.t=fun_of_a->funv->Uniform_array.sexp_of_t(Cheap_option.sexp_of_t_of_a)v[@@@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.lengthletgetti=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;;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