123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149(* File: csv_utils.ml
Copyright (C) 2017-
Christophe Troestler <Christophe.Troestler@umons.ac.be>
WWW: http://math.umons.ac.be/an/software/
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1 or
later as published by the Free Software Foundation, with the special
exception on linking described in the file LICENSE.
This library is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file
LICENSE for more details. *)(* Specialize [min] to integers for performance reasons (> 150% faster). *)letminxy=if(x:int)<=ythenxelseyletmaxxy=if(x:int)>=ythenxelsey(* Add Buffer.add_subbytes for all compiler versions.
Copied from the OCaml stdlib. *)moduleBuffer=structincludeBufferletadd_subbytesbsoffsetlen=add_substringb(Bytes.unsafe_to_strings)offsetlenend(* Enhance the List module with tail rec functions. *)moduleList=structincludeList(* Implementation of [map] in JSC Core. *)letmap_slowl~f=List.rev(List.rev_mapfl)letreccount_map~flctr=matchlwith|[]->[]|[x1]->letf1=fx1in[f1]|[x1;x2]->letf1=fx1inletf2=fx2in[f1;f2]|[x1;x2;x3]->letf1=fx1inletf2=fx2inletf3=fx3in[f1;f2;f3]|[x1;x2;x3;x4]->letf1=fx1inletf2=fx2inletf3=fx3inletf4=fx4in[f1;f2;f3;f4]|x1::x2::x3::x4::x5::tl->letf1=fx1inletf2=fx2inletf3=fx3inletf4=fx4inletf5=fx5inf1::f2::f3::f4::f5::(ifctr>1000thenmap_slow~ftlelsecount_map~ftl(ctr+1))letmapfl=count_map~fl0(* Implementation of [append] in JSC core. *)letslow_appendl1l2=List.rev_append(List.revl1)l2letreccount_appendl1l2count=matchl1with|[]->l2|[x1]->x1::l2|[x1;x2]->x1::x2::l2|[x1;x2;x3]->x1::x2::x3::l2|[x1;x2;x3;x4]->x1::x2::x3::x4::l2|x1::x2::x3::x4::x5::tl->x1::x2::x3::x4::x5::(ifcount>1000thenslow_appendtll2elsecount_appendtll2(count+1))letappendl1l2=count_appendl1l20(* Tail recursive [combine]. *)letrecrev_combineaccl1l2=matchl1,l2with|([],[])->acc|(a1::l1,a2::l2)->rev_combine((a1,a2)::acc)l1l2|(_,_)->invalid_arg"List.combine"letslow_combinel1l2=List.rev(rev_combine[]l1l2)letreccount_combinel1l2count=matchl1,l2with|([],[])->[]|([x1],[y1])->[x1,y1]|([x1;x2],[y1;y2])->[x1,y1;x2,y2]|([x1;x2;x3],[y1;y2;y3])->[x1,y1;x2,y2;x3,y3]|([x1;x2;x3;x4],[y1;y2;y3;y4])->[x1,y1;x2,y2;x3,y3;x4,y4]|(x1::x2::x3::x4::tl1),(y1::y2::y3::y4::tl2)->(x1,y1)::(x2,y2)::(x3,y3)::(x4,y4)::(ifcount>1000thenslow_combinetl1tl2elsecount_combinetl1tl2(count+1))|(_,_)->invalid_arg"List.combine"letcombinel1l2=count_combinel1l20end(*
* Helpers for input
*)letis_space_or_tabc=c=' '||c='\t'(* See documentation *)letis_real_spacec=c=' '(* when separator = '\t' *)(* Given a buffer, returns its content stripped of *final* whitespace. *)letrstrip_contentsbuf=letn=ref(Buffer.lengthbuf-1)inwhile!n>=0&&is_space_or_tab(Buffer.nthbuf!n)dodecrndone;Buffer.subbuf0(!n+1)(* Return the substring after stripping its final space. It is
assumed the substring parameters are valid. *)letrstrip_substringbufofslen=letn=ref(ofs+len-1)inwhile!n>=ofs&&is_space_or_tab(Bytes.unsafe_getbuf!n)dodecrndone;Bytes.sub_stringbufofs(!n-ofs+1)letdo_nothing_=()(* Array: char escaped with '\\' → char.
Keep in sync with [escape]. *)letunescape=letescaped_byc=matchChar.unsafe_chrcwith|'0'->'\000'(* \0 = NULL *)|'b'->'\b'|'n'->'\n'|'r'->'\r'|'t'->'\t'|'Z'->'\026'(* Ctrl + Z, used by MySQL. *)|c->c(* unchanged *)inArray.init256escaped_by