Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file span_ns.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779open!ImportopenStd_internalopen!Int63.Oletmodule_name="Core_kernel.Time_ns.Span"typeunderlying=Int63.tletarch_sixtyfour=Int.equalSys.word_size64letround_nearest=Float.int63_round_nearest_exnletfloatx=Int63.to_floatx(* [Span] is basically a [Int63]. It even silently ignores overflow. *)moduleT=structtypet=Int63.t(* nanoseconds *)[@@derivinghash,bin_io,typerep]moduleReplace_polymorphic_compare=Int63.Replace_polymorphic_compareletzero=Int63.zeroendincludeTopenReplace_polymorphic_comparemoduleParts=structtypet={sign:Sign.t;hr:int;min:int;sec:int;ms:int;us:int;ns:int}[@@derivingcompare,sexp]endletnextt=Int63.succtletprevt=Int63.predtletnanosecond=Int63.of_int1letmicrosecond=Int63.(of_int1000*nanosecond)letmillisecond=Int63.(of_int1000*microsecond)letsecond=Int63.(of_int1000*millisecond)letminute=Int63.(of_int60*second)lethour=Int63.(of_int60*minute)letday=Int63.(of_int24*hour)(* Beyond [min_value..max_value], not every microsecond can be represented as a [float]
number of seconds. (In fact, it is around 135y, but we leave a small margin.)
In the presence of silently ignored overflow, note that [t] is not actually bound to
stay between these limits. *)letmax_value=Int63.(of_int135*of_int365*day)letmin_value=Int63.negmax_valueletcreate?sign:(sign_=Sign.Pos(* rebind so not shadowed by [open Int63] below *))?day:(days=0)?(hr=0)?min:(minutes=0)?(sec=0)?(ms=0)?(us=0)?(ns=0)()=letopenInt63inlett=(of_intdays*day)+(of_inthr*hour)+(of_intminutes*minute)+(of_intsec*second)+(of_intms*millisecond)+(of_intus*microsecond)+(of_intns*nanosecond)inmatchsign_with|Neg->negt|Pos|Zero->t;;letto_partst=letopenInt63inletmag=abstin{Parts.sign=(ift<zerothenNegelseift>zerothenPoselseZero);hr=to_int_exn(mag/hour);min=to_int_exn(remmaghour/minute);sec=to_int_exn(remmagminute/second);ms=to_int_exn(remmagsecond/millisecond);us=to_int_exn(remmagmillisecond/microsecond);ns=to_int_exn(remmagmicrosecond/nanosecond)};;letof_parts{Parts.sign;hr;min;sec;ms;us;ns}=create~sign~hr~min~sec~ms~us~ns();;letof_nsf=round_nearestfletof_int63_nsi=iletof_int_usi=Int63.(of_inti*microsecond)letof_int_msi=Int63.(of_inti*millisecond)letof_int_seci=Int63.(of_inti*second)letof_usf=round_nearest(f*.floatmicrosecond)letof_msf=round_nearest(f*.floatmillisecond)letof_secf=round_nearest(f*.floatsecond)letof_minf=round_nearest(f*.floatminute)letof_hrf=round_nearest(f*.floathour)letof_dayf=round_nearest(f*.floatday)letof_sec_with_microsecond_precisionsec=letus=round_nearest(sec*.1e6)inof_int63_nsInt63.(us*of_int1000);;letof_int63_secondsx=x*secondletof_int32_secondsx=of_int63_seconds(Int63.of_int32x)letto_nst=floattletto_int63_nst=tletto_ust=floatt/.floatmicrosecondletto_mst=floatt/.floatmillisecondletto_sect=floatt/.floatsecondletto_mint=floatt/.floatminuteletto_hrt=floatt/.floathourletto_dayt=floatt/.floatdayletto_int_ust=Int63.(to_int_exn(t/microsecond))letto_int_mst=Int63.(to_int_exn(t/millisecond))letto_int_sect=Int63.(to_int_exn(t/second))letto_int63_seconds_round_down_exnt=t/%secondletof_int_ns=ifarch_sixtyfourthenfuni->of_int63_ns(Int63.of_inti)elsefun_->failwith"Time_ns.Span.of_int_ns: unsupported on 32bit machines";;letto_int_ns=ifarch_sixtyfourthenfunt->Int63.to_int_exn(to_int63_nst)elsefun_->failwith"Time_ns.Span.to_int_ns: unsupported on 32bit machines";;let(+)tu=Int63.(+)tulet(-)tu=Int63.(-)tuletabs=Int63.absletneg=Int63.negletscaletf=round_nearest(floatt*.f)letscale_int63ti=Int63.(*)tiletscale_intti=scale_int63t(Int63.of_inti)letdiv=Int63.(/%)let(/)tf=round_nearest(floatt/.f)let(//)=Int63.(//)letto_proportional_floatt=Int63.to_floattletof_unit_of_timeu=match(u:Unit_of_time.t)with|Nanosecond->nanosecond|Microsecond->microsecond|Millisecond->millisecond|Second->second|Minute->minute|Hour->hour|Day->day;;letto_unit_of_timet:Unit_of_time.t=letabs_t=abstinifabs_t>=daythenDayelseifabs_t>=hourthenHourelseifabs_t>=minutethenMinuteelseifabs_t>=secondthenSecondelseifabs_t>=millisecondthenMillisecondelseifabs_t>=microsecondthenMicrosecondelseNanosecond;;moduleStable=structmoduleV2=structmoduleT=structtypenonrect=t[@@derivingbin_io,compare,hash]letof_int63_exnt=of_int63_nstletto_int63t=to_int63_nstmoduleTo_string=structletnumber_of_digits_to_write~span_part_magnitude=letopenInt.Oinifspan_part_magnitude=0then0elseifspan_part_magnitude<10then1elseifspan_part_magnitude<100then2elseifspan_part_magnitude<1_000then3elseifspan_part_magnitude<10_000then4elseifspan_part_magnitude<100_000then5elseassertfalse;;(* span part magnitudes are always < 100_000 *)letnumber_of_decimal_places_to_write~billionths=letopenInt.Oinassert(billionths>=0&&billionths<=999_999_999);ifbillionths=0then0elseifbillionths%10<>0then9elseifbillionths%100<>0then8elseifbillionths%1_000<>0then7elseifbillionths%10_000<>0then6elseifbillionths%100_000<>0then5elseifbillionths%1_000_000<>0then4elseifbillionths%10_000_000<>0then3elseifbillionths%100_000_000<>0then2else1;;letwrite_charbuf~poschar=letopenInt.OinBytes.unsafe_setbufposchar;pos+1;;letwrite_2_charsbuf~poschar1char2=letopenInt.OinBytes.unsafe_setbufposchar1;Bytes.unsafe_setbuf(pos+1)char2;pos+2;;letwrite_digitsbuf~pos~digitsint=letopenInt.OinDigit_string_helpers.write_int63buf~pos~digits(Int63.of_intint);pos+digits;;letwrite_decimalsbuf~pos~decimals~billionths=letopenInt.OinDigit_string_helpers.write_int63buf~pos~digits:decimals(Int63.of_int(billionths/Int.pow10(9-decimals)));pos+decimals;;letwrite_if_non_emptybuf~pos~digitsintsuffix=letopenInt.Oinifdigits=0thenposelse(letpos=write_digitsbuf~pos~digitsintinletpos=write_charbuf~possuffixinpos);;letnanos_of_millisecond=to_int63_nsmillisecond|>Int63.to_int_exnletnanos_of_microsecond=to_int63_nsmicrosecond|>Int63.to_int_exnletint63_60=Int63.of_int60letint63_24=Int63.of_int24(* Units of seconds and smaller can be written in decimal notation without
worrying about non-power-of-ten factors. *)moduleDecimal_unit=structtypet=|Second|Millisecond|Microsecond|Nanosecond|None[@@derivingcompare,sexp_of]letcreate~s~ns=letopenInt.Oinifs>0thenSecondelseifns>=nanos_of_millisecondthenMillisecondelseifns>=nanos_of_microsecondthenMicrosecondelseifns>=1thenNanosecondelseNone;;letintegert~s~ns=letopenInt.Oinmatchtwith|Second->s|Millisecond->ns/nanos_of_millisecond|Microsecond->ns/nanos_of_microsecond|Nanosecond->ns|None->0;;letbillionthst~ns=letopenInt.Oinmatchtwith|Second->ns|Millisecond->ns%nanos_of_millisecond*1_000|Microsecond->ns%nanos_of_microsecond*1_000_000|Nanosecond->0|None->0;;letlengtht~digits~decimals=letopenInt.Oinletdigits_len=matchtwith|Second->digits+1|Millisecond|Microsecond|Nanosecond->digits+2|None->0inletdecimals_len=ifdecimals>0thendecimals+1else0indigits_len+decimals_len;;letwrite_suffixtbuf~pos=matchtwith|Second->write_charbuf~pos's'|Millisecond->write_2_charsbuf~pos'm''s'|Microsecond->write_2_charsbuf~pos'u''s'|Nanosecond->write_2_charsbuf~pos'n''s'|None->pos;;letwritetbuf~pos~integer~digits~billionths~decimals=letopenInt.Oinifdigits=0thenposelse(letpos=write_digitsbuf~posinteger~digitsinletpos=ifdecimals=0thenposelse(letpos=write_charbuf~pos'.'inwrite_decimalsbuf~pos~billionths~decimals)inwrite_suffixtbuf~pos);;endletto_stringt=ifequaltzerothen"0s"else(letis_negative=t<zeroinletseconds=Int63.(/)(to_int63_nst)(to_int63_nssecond)inletns=Int63.rem(to_int63_nst)(to_int63_nssecond)|>Int63.to_int_exninletseconds=Int63.abssecondsinletns=Int.absnsinlets=Int63.remsecondsint63_60|>Int63.to_int_exninletminutes=Int63.(/)secondsint63_60inletm=Int63.remminutesint63_60|>Int63.to_int_exninlethours=Int63.(/)minutesint63_60inleth=Int63.remhoursint63_24|>Int63.to_int_exninletd=Int63.(/)hoursint63_24|>Int63.to_int_exninletopenInt.Oinletdigits_of_d=number_of_digits_to_write~span_part_magnitude:dinletdigits_of_h=number_of_digits_to_write~span_part_magnitude:hinletdigits_of_m=number_of_digits_to_write~span_part_magnitude:minletdecimal_unit=Decimal_unit.create~s~nsinletdecimal_unit_integer=Decimal_unit.integerdecimal_unit~s~nsinletdecimal_unit_billionths=Decimal_unit.billionthsdecimal_unit~nsinletdigits_of_decimal_unit=number_of_digits_to_write~span_part_magnitude:decimal_unit_integerinletdecimals_of_decimal_unit=number_of_decimal_places_to_write~billionths:decimal_unit_billionthsinletstring_length=letsign_len=ifis_negativethen1else0inletd_len=ifdigits_of_d>0thendigits_of_d+1else0inleth_len=ifdigits_of_h>0thendigits_of_h+1else0inletm_len=ifdigits_of_m>0thendigits_of_m+1else0inletdecimal_unit_len=Decimal_unit.lengthdecimal_unit~digits:digits_of_decimal_unit~decimals:decimals_of_decimal_unitinsign_len+d_len+h_len+m_len+decimal_unit_leninassert(string_length>0);letbuf=Bytes.createstring_lengthinletpos=0inletpos=ifis_negativethenwrite_charbuf~pos'-'elseposinletpos=write_if_non_emptybuf~pos~digits:digits_of_dd'd'inletpos=write_if_non_emptybuf~pos~digits:digits_of_hh'h'inletpos=write_if_non_emptybuf~pos~digits:digits_of_mm'm'inletpos=Decimal_unit.writedecimal_unitbuf~pos~integer:decimal_unit_integer~digits:digits_of_decimal_unit~billionths:decimal_unit_billionths~decimals:decimals_of_decimal_unitinassert(pos=string_length);Bytes.unsafe_to_string~no_mutation_while_string_reachable:buf);;endletto_string=To_string.to_stringmoduleOf_string=struct(* We do computations using negative numbers everywhere and test against
things related to [Int63.min_value] rather than using positive numbers
and testing against things related to [Int63.max_value] because the
negative integer range is one wider than the positive integer range
(-2**63 vs 2**63-1), and we need that to be able to handle Int63.min_value
nicely. *)letint63_10=Int63.of_int10letmin_mult10_without_underflow=Int63.(min_value/int63_10)let[@inlinenever]invalid_stringstring~reason=raise_s[%message"Time_ns.Span.of_string: invalid string"(string:string)(reason:string)];;(* Assumes x and y are both nonpositive *)letadd_without_underflow~stringxy=letopenInt63.Oinletsum=x+yinifsum>xtheninvalid_stringstring~reason:"span would be outside of int63 range";sum;;letadd_neg_digit~stringint63char=letopenInt63.Oinletdigit=Int63.of_int(Char.get_digit_exnchar)inifint63<min_mult10_without_underflowtheninvalid_stringstring~reason:"span would be outside of int63 range";add_without_underflow~string(int63*int63_10)(-digit);;letmin_factor_ofspan=Int63.(/)Int63.min_value(to_int63_nsspan)letmin_days_without_underflow=min_factor_ofdayletmin_hours_without_underflow=min_factor_ofhourletmin_minutes_without_underflow=min_factor_ofminuteletmin_seconds_without_underflow=min_factor_ofsecondletmin_milliseconds_without_underflow=min_factor_ofmillisecondletmin_microseconds_without_underflow=min_factor_ofmicrosecondletmin_nanoseconds_without_underflow=min_factor_ofnanosecondletmin_without_underflow_of_unit_of_timeunit_of_time=match(unit_of_time:Unit_of_time.t)with|Day->min_days_without_underflow|Hour->min_hours_without_underflow|Minute->min_minutes_without_underflow|Second->min_seconds_without_underflow|Millisecond->min_milliseconds_without_underflow|Microsecond->min_microseconds_without_underflow|Nanosecond->min_nanoseconds_without_underflow;;letnegative_partstring~neg_integer~decimal_pos~end_pos~unit_of_time~round_ties_before_negating=letopenInt.Oinletscale=to_int63_ns(of_unit_of_timeunit_of_time)inletmin_without_underflow=min_without_underflow_of_unit_of_timeunit_of_timeinifInt63.(<)neg_integermin_without_underflowtheninvalid_stringstring~reason:"span would be outside of int63 range";letneg_integer_ns=Int63.(*)neg_integerscaleinletfraction_pos=decimal_pos+1iniffraction_pos>=end_posthenneg_integer_nselse(letdecimal_ns=Digit_string_helpers.read_int63_decimalstring~pos:fraction_pos~scale~decimals:(end_pos-fraction_pos)~allow_underscore:true~round_ties:round_ties_before_negatinginadd_without_underflow~stringneg_integer_ns(Int63.(~-)decimal_ns));;letof_stringstring=letopenInt.Oinletneg_ns=refInt63.zeroinletpos=ref0inletlen=String.lengthstringiniflen=0theninvalid_stringstring~reason:"empty string";letis_negative=matchString.unsafe_getstring!poswith|'-'->incrpos;true|'+'->incrpos;false|_->falseinletround_ties_before_negating:Digit_string_helpers.Round.t=(* Ultimately, we always round parsed spans towards positive infinity when
the nearest round ns are equidistant. For example, "1.5ns" is read as
2.0ns, and "-1.5ns" is read as -1ns. Since we read absolute values before
applying the sign, we must choose our rounding direction based on the
sign. Rounding decimal values happens before negating their magnitude. *)matchis_negativewith|false->Toward_positive_infinity|true->Toward_negative_infinityin(* Loop over parts, like "5m" in "1h5m30s" *)while!pos<lendolethas_digit=reffalseinletneg_integer=leti=refInt63.zeroinletend_of_digits=reffalseinwhile!pos<len&¬!end_of_digitsdoletc=String.unsafe_getstring!posinmatchcwith|'0'..'9'->i:=add_neg_digit~string!ic;has_digit:=true;incrpos|'_'->incrpos|_->end_of_digits:=truedone;!iinletdecimal_pos=!posinif!pos<len&&Char.equal'.'(String.unsafe_getstring!pos)then(incrpos;letend_of_decimals=reffalseinwhile!pos<len&¬!end_of_decimalsdomatchString.unsafe_getstring!poswith|'0'..'9'->has_digit:=true;incrpos|'_'->incrpos|_->end_of_decimals:=truedone);letend_pos=!posinifnot!has_digittheninvalid_stringstring~reason:"no digits before unit suffix";letunit_of_time:Unit_of_time.t=if!pos+1<len&&Char.equal's'(String.unsafe_getstring(!pos+1))then(matchString.unsafe_getstring!poswith|'m'->pos:=!pos+2;Millisecond|'u'->pos:=!pos+2;Microsecond|'n'->pos:=!pos+2;Nanosecond|_->invalid_stringstring~reason:"unparseable unit suffix")elseif!pos<lenthen(matchString.unsafe_getstring!poswith|'d'->incrpos;Day|'h'->incrpos;Hour|'m'->incrpos;Minute|'s'->incrpos;Second|_->invalid_stringstring~reason:"unparseable unit suffix")elseinvalid_stringstring~reason:"no unit suffix after digits"inletneg_nanos_of_part=negative_partstring~neg_integer~decimal_pos~end_pos~unit_of_time~round_ties_before_negatinginneg_ns:=add_without_underflow~string!neg_nsneg_nanos_of_partdone;letns=ifis_negativethen!neg_nselseifInt63.(=)!neg_nsInt63.min_valuetheninvalid_stringstring~reason:"span would be outside of int63 range"elseInt63.(~-)!neg_nsinof_int63_nsns;;endletof_string=Of_string.of_stringletsexp_of_tt=Sexp.Atom(to_stringt)lett_of_sexpsexp=matchsexpwith|Sexp.Atomx->(tryof_stringxwith|exn->of_sexp_error(Exn.to_stringexn)sexp)|Sexp.List_->of_sexp_error"Time_ns.Span.Stable.V2.t_of_sexp: sexp must be an Atom"sexp;;endincludeTincludeComparator.Stable.V1.Make(T)endendletto_string=Stable.V2.to_stringletof_string=Stable.V2.of_stringletsexp_of_t=Stable.V2.sexp_of_tlett_of_sexp=Stable.V2.t_of_sexpmoduleAlternate_sexp=structtypenonrect=t[@@derivingsexp]endincludeComparable.Validate_with_zero(structtypenonrect=t[@@derivingcompare,sexp]letzero=zeroend)(* Functions required by [Robustly_comparable]: allows for [robust_comparison_tolerance]
granularity.
A microsecond is a reasonable granularity because there is very little network
activity that can be measured to sub-microsecond resolution. *)letrobust_comparison_tolerance=microsecondlet(>=.)tu=t>=Int63.(u-robust_comparison_tolerance)let(<=.)tu=t<=Int63.(u+robust_comparison_tolerance)let(=.)tu=Int63.(abs(t-u))<=robust_comparison_tolerancelet(>.)tu=t>Int63.(u+robust_comparison_tolerance)let(<.)tu=t<Int63.(u-robust_comparison_tolerance)let(<>.)tu=Int63.(abs(t-u))>robust_comparison_toleranceletrobustly_comparetu=ift<.uthen-1elseift>.uthen1else0(* We don't just convert to [Time.Span.t] and use the conversion there because our
[to_span] conversion is limited to microsecond precision. *)letto_string_hum?(delimiter='_')?(decimals=3)?(align_decimal=false)?unit_of_timet=letfloat,suffix=matchOption.valueunit_of_time~default:(to_unit_of_timet)with|Day->to_dayt,"d"|Hour->to_hrt,"h"|Minute->to_mint,"m"|Second->to_sect,"s"|Millisecond->to_mst,"ms"|Microsecond->to_ust,"us"|Nanosecond->to_nst,"ns"inletprefix=Float.to_string_humfloat~delimiter~decimals~strip_zero:(notalign_decimal)inletsuffix=ifalign_decimal&&Int.(=)(String.lengthsuffix)1thensuffix^" "elsesuffixinprefix^suffix;;letsince_unix_epoch()=Time_now.nanoseconds_since_unix_epoch()|>of_int63_nsletrandom?state()=Int63.random?state(max_value+Int63.one)-Int63.random?state(negmin_value+Int63.one);;letrandomizet~percent=Span_helpers.randomizet~percent~scaleletto_short_stringt=let({sign;hr;min;sec;ms;us;ns}:Parts.t)=to_partstinSpan_helpers.short_string~sign~hr~min~sec~ms~us~ns;;includePretty_printer.Register(structtypenonrect=tletto_string=to_stringletmodule_name=module_nameend)includeHashable.Make_binable(structtypenonrect=t[@@derivingbin_io,compare,hash,sexp]end)moduleC=Comparable.Make_binable(structtypenonrect=t[@@derivingbin_io,compare,sexp]end)include(C:moduletypeofCwithmoduleReplace_polymorphic_compare:=Replace_polymorphic_compare)(* re-include comparisons to shadow the un-inlineable ones from [Comparable] *)includeReplace_polymorphic_compareletto_span_float_round_nearestt=Span_float.of_sec(to_sect)letof_span_float_round_nearests=of_sec(Span_float.to_secs)lethalf_microsecond=Int63.of_int500letnearest_microsecondt=Int63.((to_int63_nst+half_microsecond)/%of_int1000)let[@inlinenever]invalid_ranget=raise_s[%message"Span.t exceeds limits"(t:t)(min_value:t)(max_value:t)];;letcheck_ranget=ift<min_value||t>max_valuetheninvalid_rangetelsetletto_span_float_round_nearest_microsecondt=Span_float.of_us(Int63.to_float(nearest_microsecond(check_ranget)));;letmin_span_float_value=to_span_float_round_nearestmin_valueletmax_span_float_value=to_span_float_round_nearestmax_valueletof_span_float_round_nearest_microseconds=ifSpan_float.(>)smax_span_float_value||Span_float.(<)smin_span_float_valuethenfailwiths"Time_ns.Span does not support this span"s[%sexp_of:Span_float.t];(* Using [Time.Span.to_sec] (being the identity) so that
we make don't apply too many conversion
- Too many : `[Span.t] -> [a] -> [t]`
- Only One : `[Span.t]==[a] -> [t]`. *)of_sec_with_microsecond_precision(Span_float.to_secs);;modulePrivate=structmoduleParts=Partsletcheck_range=check_rangeletof_parts=of_partsletto_parts=to_partsend(* Legacy definitions that round to the nearest microsecond. *)letof_span=of_span_float_round_nearest_microsecondletto_span=to_span_float_round_nearest_microsecond