123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170(* What the Go source takes from the unicode/utf8 package, over a text that is a range of a [Bytes.t] or of a
[string]. The Object Pascal port has the same functions in Corvus.JsonSchema.EcmaRegex.Utf8.
A decoded character is returned as one [int], the code point above its width in bytes ([(r lsl 3) lor w]), because
a pair would be allocated. {!rune} and {!width} take it apart. *)typetext={mutableb:Bytes.t;mutables:string;mutableis_string:bool}(** The text of a match. Exactly one of [b] and [s] is in use, and [is_string] says which. One record serves both kinds
of text, so the matcher is written once and reads a byte through one branch that the processor predicts. *)(** U+FFFD, which a malformed byte of the text decodes to. *)letrune_error=0xFFFD(** Bytes below this are one character each. *)letrune_self=0x80letnew_text()={b=Bytes.empty;s="";is_string=true}letbyteti=ift.is_stringthenChar.code(String.gett.si)elseChar.code(Bytes.gett.bi)letrunepacked=packedlsr3letwidthpacked=packedland7leterror1=(rune_errorlsl3)lor1(** [decode t at limit] reads the character at [at] of the text, which ends before [limit]. [at] is below [limit].
Malformed UTF-8 is U+FFFD with a width of one byte, as utf8.DecodeRune of Go reads it: an encoding that is too long,
a surrogate and a value beyond U+10FFFF are all malformed. *)letdecodetatlimit=letp0=bytetatinifp0<0x80then(p0lsl3)lor1elseifp0<0xC2||p0>0xF4thenerror1elseletn=limit-atinifp0<0xE0thenifn<2thenerror1elseletb1=bytet(at+1)inifb1<0x80||b1>0xBFthenerror1else((((p0land0x1F)lsl6)lor(b1land0x3F))lsl3)lor2elseifp0<0xF0thenifn<3thenerror1elseletb1=bytet(at+1)in(* E0 must not start an encoding that is too long, and ED must not start a surrogate. *)if(b1<ifp0=0xE0then0xA0else0x80)||b1>ifp0=0xEDthen0x9Felse0xBFthenerror1elseletb2=bytet(at+2)inifb2<0x80||b2>0xBFthenerror1else((((p0land0x0F)lsl12)lor((b1land0x3F)lsl6)lor(b2land0x3F))lsl3)lor3elseifn<4thenerror1elseletb1=bytet(at+1)in(* F0 must not start an encoding that is too long, and F4 must not pass U+10FFFF. *)if(b1<ifp0=0xF0then0x90else0x80)||b1>ifp0=0xF4then0x8Felse0xBFthenerror1elseletb2=bytet(at+2)inifb2<0x80||b2>0xBFthenerror1elseletb3=bytet(at+3)inifb3<0x80||b3>0xBFthenerror1else((((p0land0x07)lsl18)lor((b1land0x3F)lsl12)lor((b2land0x3F)lsl6)lor(b3land0x3F))lsl3)lor4(* The first position at or above [lim], going down from [start], that holds a byte that can start a character. *)letrecrune_starttstartlim=ifstart<limthenlimelseifbytetstartland0xC0<>0x80thenstartelserune_startt(start-1)lim(** [decode_last t lo limit] reads the character that ends before [limit] in the text that starts at [lo]. [limit] is
above [lo]. It is utf8.DecodeLastRune of Go. *)letdecode_lasttlolimit=letr=bytet(limit-1)inifr<0x80then(rlsl3)lor1elseletlim=iflimit-4>lothenlimit-4elseloinletstart=rune_startt(limit-2)liminletpacked=decodetstartlimitinifstart+widthpacked<>limitthenerror1elsepacked(* Matching by UTF-16 code unit over UTF-8 text (units.go of the Go package).
ECMA-262 matches a pattern that has no u flag a code unit at a time. The engine reads a pattern without the u flag
only when the u flag grammar refuses it, and such a pattern is matched that way. A character of one, two or three
bytes of UTF-8 is one code unit. A character of four bytes is two, a high surrogate and a low one, so the matcher
has to be able to stand between them. That position is written as the index of the character's third byte. Each
half then spans two bytes, every position is still an index into the text, a group is still a pair of indexes, and
no UTF-16 copy of the text is made.
The third byte of a character is a continuation byte, which is no position at all when matching by code point, so
the two readings cannot be confused. Only a text with a character of four bytes differs between the two, and the
instructions that compare bytes (a literal run, a word boundary, a line terminator) need no change: none of them
can hold at the position between the halves, and all of them are right at every other. *)(** The two UTF-16 code units of a code point beyond the Basic Multilingual Plane. *)lethigh_surrogater=0xD800+((r-0x10000)lsr10)letlow_surrogater=0xDC00+((r-0x10000)land0x3FF)(** [unit_at t lo at hi] reads the code unit at [at] of the text, which is the bytes [lo] up to [hi]. [at] is below
[hi]. A half of a character of four bytes has a width of two. *)letunit_attloathi=letc=bytetatinifc>=0xF0thenletpacked=decodetathiinifwidthpacked=4then(high_surrogate(runepacked)lsl3)lor2elseerror1elseifc>=0x80&&c<0xC0&&at-lo>=2&&bytet(at-2)>=0xF0then(* Between the halves of the character that starts two bytes back. *)letpacked=decodet(at-2)hiinifwidthpacked=4then(low_surrogate(runepacked)lsl3)lor2elsedecodetathielsedecodetathi(** [unit_before t lo at hi] reads the code unit that ends at [at] of the text, which is the bytes [lo] up to [hi]. [at]
is above [lo]. *)letunit_beforetloathi=letpacked=decode_lasttloatinifwidthpacked=4then(low_surrogate(runepacked)lsl3)lor2elseifpacked=error1&&at-lo>=2&&hi-at>=2&&bytet(at-2)>=0xF0then(* Between the halves of the character that starts two bytes back. *)letwhole=decodet(at-2)hiinifwidthwhole=4then(high_surrogate(runewhole)lsl3)lor2elsepackedelsepacked(** Reports whether a code point has an encoding: it is in range and not a surrogate. *)letvalid_runer=(r>=0&&r<0xD800)||(r>=0xE000&&r<=0x10FFFF)(** The number of bytes of the UTF-8 encoding of a code point that has one. *)letrune_lengthr=ifr<0x80then1elseifr<0x800then2elseifr<0x10000then3else4(** Appends the UTF-8 encoding of a code point. One that has none is written as U+FFFD, as utf8.AppendRune of Go does.
*)letadd_runebufferr=letr=ifvalid_runerthenrelserune_errorinletaddc=Buffer.add_charbuffer(Char.chrc)inifr<0x80thenaddrelseifr<0x800thenbeginadd(0xC0lor(rlsr6));add(0x80lor(rland0x3F))endelseifr<0x10000thenbeginadd(0xE0lor(rlsr12));add(0x80lor((rlsr6)land0x3F));add(0x80lor(rland0x3F))endelsebeginadd(0xF0lor(rlsr18));add(0x80lor((rlsr12)land0x3F));add(0x80lor((rlsr6)land0x3F));add(0x80lor(rland0x3F))end(** [runes s units] is the code points of a string, with U+FFFD for each malformed byte. It is the conversion to a slice
of runes of Go. With [units] it is the UTF-16 code units of the string: a character beyond the Basic Multilingual
Plane is its two surrogates. (Two units are no more elements than the four bytes of the character.) *)letrunessunits=lett={b=Bytes.empty;s;is_string=true}inletn=String.lengthsinletout=Array.maken0inletcount=ref0andat=ref0inwhile!at<ndoletpacked=decodet!atninletr=runepackedinifunits&&r>0xFFFFthenbeginout.(!count)<-high_surrogater;out.(!count+1)<-low_surrogater;count:=!count+2endelsebeginout.(!count)<-r;incrcountend;at:=!at+widthpackeddone;Array.subout0!count