123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354[@@@ocaml.warning"-3"](* blit_string doesn't exist in [StdLabels.Bytes]... *)letbytes_blit_string~src~src_pos~dst~dst_pos~len=Bytes.blit_stringsrcsrc_posdstdst_poslen;;openStdLabelsopenFormat(** Type of S-expressions *)typet=|Atomofstring|Listoftlistletsexp_of_tt=tlett_of_sexpt=tletreccompare_listab=matcha,bwith|[],[]->0|[],_->-1|_,[]->1|x::xs,y::ys->letres=comparexyinifres<>0thenreselsecompare_listxsysandcompareab=ifa==bthen0else(matcha,bwith|Atoma,Atomb->String.compareab|Atom_,_->-1|_,Atom_->1|Lista,Listb->compare_listab);;letequalab=compareab=0exceptionNot_found_softexceptionOf_sexp_errorofexn*tmodulePrinting=struct(* Default indentation level for human-readable conversions *)letdefault_indent=ref1(* Escaping of strings used as atoms in S-expressions *)letmust_escapestr=letlen=String.lengthstrinlen=0||letrecloopstrix=matchstr.[ix]with|'"'|'('|')'|';'|'\\'->true|'|'->ix>0&&letnext=ix-1inChar.equalstr.[next]'#'||loopstrnext|'#'->ix>0&&letnext=ix-1inChar.equalstr.[next]'|'||loopstrnext|'\000'..'\032'|'\127'..'\255'->true|_->ix>0&&loopstr(ix-1)inloopstr(len-1);;letescapeds=letn=ref0infori=0toString.lengths-1don:=!n+matchString.unsafe_getsiwith|'\"'|'\\'|'\n'|'\t'|'\r'|'\b'->2|' '..'~'->1|_->4done;if!n=String.lengthsthenselse(lets'=Bytes.create!ninn:=0;fori=0toString.lengths-1do(matchString.unsafe_getsiwith|('\"'|'\\')asc->Bytes.unsafe_sets'!n'\\';incrn;Bytes.unsafe_sets'!nc|'\n'->Bytes.unsafe_sets'!n'\\';incrn;Bytes.unsafe_sets'!n'n'|'\t'->Bytes.unsafe_sets'!n'\\';incrn;Bytes.unsafe_sets'!n't'|'\r'->Bytes.unsafe_sets'!n'\\';incrn;Bytes.unsafe_sets'!n'r'|'\b'->Bytes.unsafe_sets'!n'\\';incrn;Bytes.unsafe_sets'!n'b'|' '..'~'asc->Bytes.unsafe_sets'!nc|c->leta=Char.codecinBytes.unsafe_sets'!n'\\';incrn;Bytes.unsafe_sets'!n(Char.chr(48+(a/100)));incrn;Bytes.unsafe_sets'!n(Char.chr(48+(a/10mod10)));incrn;Bytes.unsafe_sets'!n(Char.chr(48+(amod10))));incrndone;Bytes.unsafe_to_strings');;letesc_strstr=letestr=escapedstrinletelen=String.lengthestrinletres=Bytes.create(elen+2)inbytes_blit_string~src:estr~src_pos:0~dst:res~dst_pos:1~len:elen;Bytes.unsafe_setres0'"';Bytes.unsafe_setres(elen+1)'"';Bytes.unsafe_to_stringres;;letindex_of_newlinestrstart=String.index_from_optstrstart'\n'letget_substringstrindexend_pos_opt=letend_pos=matchend_pos_optwith|None->String.lengthstr|Someend_pos->end_posinString.substr~pos:index~len:(end_pos-index);;letis_one_linestr=matchindex_of_newlinestr0with|None->true|Someindex->index+1=String.lengthstr;;letpp_hum_maybe_esc_strppfstr=ifnot(must_escapestr)thenpp_print_stringppfstrelseifis_one_linestrthenpp_print_stringppf(esc_strstr)else(letrecloopindex=letnext_newline=index_of_newlinestrindexinletnext_line=get_substringstrindexnext_newlineinpp_print_stringppf(escapednext_line);matchnext_newlinewith|None->()|Somenewline_index->pp_print_stringppf"\\";pp_force_newlineppf();pp_print_stringppf"\\n";loop(newline_index+1)inpp_open_boxppf0;(* the leading space is to line up the lines *)pp_print_stringppf" \"";loop0;pp_print_stringppf"\"";pp_close_boxppf());;letmach_maybe_esc_strstr=ifmust_escapestrthenesc_strstrelsestr(* Output of S-expressions to formatters *)letrecpp_hum_indentindentppf=function|Atomstr->pp_hum_maybe_esc_strppfstr|List(h::t)->pp_open_boxppfindent;pp_print_stringppf"(";pp_hum_indentindentppfh;pp_hum_restindentppft|List[]->pp_print_stringppf"()"andpp_hum_restindentppf=function|h::t->pp_print_spaceppf();pp_hum_indentindentppfh;pp_hum_restindentppft|[]->pp_print_stringppf")";pp_close_boxppf();;letrecpp_mach_internalmay_need_spaceppf=function|Atomstr->letstr'=mach_maybe_esc_strstrinletnew_may_need_space=str'==strinifmay_need_space&&new_may_need_spacethenpp_print_stringppf" ";pp_print_stringppfstr';new_may_need_space|List(h::t)->pp_print_stringppf"(";letmay_need_space=pp_mach_internalfalseppfhinpp_mach_restmay_need_spaceppft;false|List[]->pp_print_stringppf"()";falseandpp_mach_restmay_need_spaceppf=function|h::t->letmay_need_space=pp_mach_internalmay_need_spaceppfhinpp_mach_restmay_need_spaceppft|[]->pp_print_stringppf")";;letpp_humppfsexp=pp_hum_indent!default_indentppfsexpletpp_machppfsexp=ignore(pp_mach_internalfalseppfsexp)letpp=pp_mach(* Sexp size *)letrecsize_loop((v,c)asacc)=function|Atomstr->v+1,c+String.lengthstr|Listlst->List.fold_leftlst~init:acc~f:size_loop;;letsizesexp=size_loop(0,0)sexp(* Buffer conversions *)letto_buffer_hum~buf?(indent=!default_indent)sexp=letppf=Format.formatter_of_bufferbufinFormat.fprintfppf"%a@?"(pp_hum_indentindent)sexp;;letto_buffer_mach~bufsexp=letrecloopmay_need_space=function|Atomstr->letstr'=mach_maybe_esc_strstrinletnew_may_need_space=str'==strinifmay_need_space&&new_may_need_spacethenBuffer.add_charbuf' ';Buffer.add_stringbufstr';new_may_need_space|List(h::t)->Buffer.add_charbuf'(';letmay_need_space=loopfalsehinloop_restmay_need_spacet;false|List[]->Buffer.add_stringbuf"()";falseandloop_restmay_need_space=function|h::t->letmay_need_space=loopmay_need_spacehinloop_restmay_need_spacet|[]->Buffer.add_charbuf')'inignore(loopfalsesexp);;letto_buffer=to_buffer_machletto_buffer_gen~buf~add_char~add_stringsexp=letrecloopmay_need_space=function|Atomstr->letstr'=mach_maybe_esc_strstrinletnew_may_need_space=str'==strinifmay_need_space&&new_may_need_spacethenadd_charbuf' ';add_stringbufstr';new_may_need_space|List(h::t)->add_charbuf'(';letmay_need_space=loopfalsehinloop_restmay_need_spacet;false|List[]->add_stringbuf"()";falseandloop_restmay_need_space=function|h::t->letmay_need_space=loopmay_need_spacehinloop_restmay_need_spacet|[]->add_charbuf')'inignore(loopfalsesexp);;(* The maximum size of a thing on the minor heap is 256 words.
Previously, this size of the returned buffer here was 4096 bytes, which
caused the Buffer to be allocated on the *major* heap every time.
According to a simple benchmark by Ron, we can improve performance for
small s-expressions by a factor of ~4 if we only allocate 1024 bytes
(128 words + some small overhead) worth of buffer initially. And one
can argue that if it's free to allocate strings smaller than 256 words,
large s-expressions requiring larger expensive buffers won't notice
the extra two doublings from 1024 bytes to 2048 and 4096. And especially
performance-sensitive applications to always pass in a larger buffer to
use. *)letbuffer()=Buffer.create1024(* String conversions *)letto_string_hum?indent=function|Atomstrwhenmatchindex_of_newlinestr0with|None->true|Some_->false->mach_maybe_esc_strstr|sexp->letbuf=buffer()into_buffer_hum?indentsexp~buf;Buffer.contentsbuf;;letto_string_mach=function|Atomstr->mach_maybe_esc_strstr|sexp->letbuf=buffer()into_buffer_machsexp~buf;Buffer.contentsbuf;;letto_string=to_string_machendincludePrintingletof_float_style:[`Underscores|`No_underscores]ref=ref`No_underscoresletof_int_style:[`Underscores|`No_underscores]ref=ref`No_underscoresmodulePrivate=structincludePrintingendletmessagenamefields=letrecconv_fields=function|[]->[]|(fname,fsexp)::rest->(matchfnamewith|""->fsexp::conv_fieldsrest|_->List[Atomfname;fsexp]::conv_fieldsrest)inList(Atomname::conv_fieldsfields);;