...

Source file src/pkg/vendor/golang.org/x/text/unicode/norm/composition.go

     1	// Copyright 2011 The Go Authors. All rights reserved.
     2	// Use of this source code is governed by a BSD-style
     3	// license that can be found in the LICENSE file.
     4	
     5	package norm
     6	
     7	import "unicode/utf8"
     8	
     9	const (
    10		maxNonStarters = 30
    11		// The maximum number of characters needed for a buffer is
    12		// maxNonStarters + 1 for the starter + 1 for the GCJ
    13		maxBufferSize    = maxNonStarters + 2
    14		maxNFCExpansion  = 3  // NFC(0x1D160)
    15		maxNFKCExpansion = 18 // NFKC(0xFDFA)
    16	
    17		maxByteBufferSize = utf8.UTFMax * maxBufferSize // 128
    18	)
    19	
    20	// ssState is used for reporting the segment state after inserting a rune.
    21	// It is returned by streamSafe.next.
    22	type ssState int
    23	
    24	const (
    25		// Indicates a rune was successfully added to the segment.
    26		ssSuccess ssState = iota
    27		// Indicates a rune starts a new segment and should not be added.
    28		ssStarter
    29		// Indicates a rune caused a segment overflow and a CGJ should be inserted.
    30		ssOverflow
    31	)
    32	
    33	// streamSafe implements the policy of when a CGJ should be inserted.
    34	type streamSafe uint8
    35	
    36	// first inserts the first rune of a segment. It is a faster version of next if
    37	// it is known p represents the first rune in a segment.
    38	func (ss *streamSafe) first(p Properties) {
    39		*ss = streamSafe(p.nTrailingNonStarters())
    40	}
    41	
    42	// insert returns a ssState value to indicate whether a rune represented by p
    43	// can be inserted.
    44	func (ss *streamSafe) next(p Properties) ssState {
    45		if *ss > maxNonStarters {
    46			panic("streamSafe was not reset")
    47		}
    48		n := p.nLeadingNonStarters()
    49		if *ss += streamSafe(n); *ss > maxNonStarters {
    50			*ss = 0
    51			return ssOverflow
    52		}
    53		// The Stream-Safe Text Processing prescribes that the counting can stop
    54		// as soon as a starter is encountered. However, there are some starters,
    55		// like Jamo V and T, that can combine with other runes, leaving their
    56		// successive non-starters appended to the previous, possibly causing an
    57		// overflow. We will therefore consider any rune with a non-zero nLead to
    58		// be a non-starter. Note that it always hold that if nLead > 0 then
    59		// nLead == nTrail.
    60		if n == 0 {
    61			*ss = streamSafe(p.nTrailingNonStarters())
    62			return ssStarter
    63		}
    64		return ssSuccess
    65	}
    66	
    67	// backwards is used for checking for overflow and segment starts
    68	// when traversing a string backwards. Users do not need to call first
    69	// for the first rune. The state of the streamSafe retains the count of
    70	// the non-starters loaded.
    71	func (ss *streamSafe) backwards(p Properties) ssState {
    72		if *ss > maxNonStarters {
    73			panic("streamSafe was not reset")
    74		}
    75		c := *ss + streamSafe(p.nTrailingNonStarters())
    76		if c > maxNonStarters {
    77			return ssOverflow
    78		}
    79		*ss = c
    80		if p.nLeadingNonStarters() == 0 {
    81			return ssStarter
    82		}
    83		return ssSuccess
    84	}
    85	
    86	func (ss streamSafe) isMax() bool {
    87		return ss == maxNonStarters
    88	}
    89	
    90	// GraphemeJoiner is inserted after maxNonStarters non-starter runes.
    91	const GraphemeJoiner = "\u034F"
    92	
    93	// reorderBuffer is used to normalize a single segment.  Characters inserted with
    94	// insert are decomposed and reordered based on CCC. The compose method can
    95	// be used to recombine characters.  Note that the byte buffer does not hold
    96	// the UTF-8 characters in order.  Only the rune array is maintained in sorted
    97	// order. flush writes the resulting segment to a byte array.
    98	type reorderBuffer struct {
    99		rune  [maxBufferSize]Properties // Per character info.
   100		byte  [maxByteBufferSize]byte   // UTF-8 buffer. Referenced by runeInfo.pos.
   101		nbyte uint8                     // Number or bytes.
   102		ss    streamSafe                // For limiting length of non-starter sequence.
   103		nrune int                       // Number of runeInfos.
   104		f     formInfo
   105	
   106		src      input
   107		nsrc     int
   108		tmpBytes input
   109	
   110		out    []byte
   111		flushF func(*reorderBuffer) bool
   112	}
   113	
   114	func (rb *reorderBuffer) init(f Form, src []byte) {
   115		rb.f = *formTable[f]
   116		rb.src.setBytes(src)
   117		rb.nsrc = len(src)
   118		rb.ss = 0
   119	}
   120	
   121	func (rb *reorderBuffer) initString(f Form, src string) {
   122		rb.f = *formTable[f]
   123		rb.src.setString(src)
   124		rb.nsrc = len(src)
   125		rb.ss = 0
   126	}
   127	
   128	func (rb *reorderBuffer) setFlusher(out []byte, f func(*reorderBuffer) bool) {
   129		rb.out = out
   130		rb.flushF = f
   131	}
   132	
   133	// reset discards all characters from the buffer.
   134	func (rb *reorderBuffer) reset() {
   135		rb.nrune = 0
   136		rb.nbyte = 0
   137	}
   138	
   139	func (rb *reorderBuffer) doFlush() bool {
   140		if rb.f.composing {
   141			rb.compose()
   142		}
   143		res := rb.flushF(rb)
   144		rb.reset()
   145		return res
   146	}
   147	
   148	// appendFlush appends the normalized segment to rb.out.
   149	func appendFlush(rb *reorderBuffer) bool {
   150		for i := 0; i < rb.nrune; i++ {
   151			start := rb.rune[i].pos
   152			end := start + rb.rune[i].size
   153			rb.out = append(rb.out, rb.byte[start:end]...)
   154		}
   155		return true
   156	}
   157	
   158	// flush appends the normalized segment to out and resets rb.
   159	func (rb *reorderBuffer) flush(out []byte) []byte {
   160		for i := 0; i < rb.nrune; i++ {
   161			start := rb.rune[i].pos
   162			end := start + rb.rune[i].size
   163			out = append(out, rb.byte[start:end]...)
   164		}
   165		rb.reset()
   166		return out
   167	}
   168	
   169	// flushCopy copies the normalized segment to buf and resets rb.
   170	// It returns the number of bytes written to buf.
   171	func (rb *reorderBuffer) flushCopy(buf []byte) int {
   172		p := 0
   173		for i := 0; i < rb.nrune; i++ {
   174			runep := rb.rune[i]
   175			p += copy(buf[p:], rb.byte[runep.pos:runep.pos+runep.size])
   176		}
   177		rb.reset()
   178		return p
   179	}
   180	
   181	// insertOrdered inserts a rune in the buffer, ordered by Canonical Combining Class.
   182	// It returns false if the buffer is not large enough to hold the rune.
   183	// It is used internally by insert and insertString only.
   184	func (rb *reorderBuffer) insertOrdered(info Properties) {
   185		n := rb.nrune
   186		b := rb.rune[:]
   187		cc := info.ccc
   188		if cc > 0 {
   189			// Find insertion position + move elements to make room.
   190			for ; n > 0; n-- {
   191				if b[n-1].ccc <= cc {
   192					break
   193				}
   194				b[n] = b[n-1]
   195			}
   196		}
   197		rb.nrune += 1
   198		pos := uint8(rb.nbyte)
   199		rb.nbyte += utf8.UTFMax
   200		info.pos = pos
   201		b[n] = info
   202	}
   203	
   204	// insertErr is an error code returned by insert. Using this type instead
   205	// of error improves performance up to 20% for many of the benchmarks.
   206	type insertErr int
   207	
   208	const (
   209		iSuccess insertErr = -iota
   210		iShortDst
   211		iShortSrc
   212	)
   213	
   214	// insertFlush inserts the given rune in the buffer ordered by CCC.
   215	// If a decomposition with multiple segments are encountered, they leading
   216	// ones are flushed.
   217	// It returns a non-zero error code if the rune was not inserted.
   218	func (rb *reorderBuffer) insertFlush(src input, i int, info Properties) insertErr {
   219		if rune := src.hangul(i); rune != 0 {
   220			rb.decomposeHangul(rune)
   221			return iSuccess
   222		}
   223		if info.hasDecomposition() {
   224			return rb.insertDecomposed(info.Decomposition())
   225		}
   226		rb.insertSingle(src, i, info)
   227		return iSuccess
   228	}
   229	
   230	// insertUnsafe inserts the given rune in the buffer ordered by CCC.
   231	// It is assumed there is sufficient space to hold the runes. It is the
   232	// responsibility of the caller to ensure this. This can be done by checking
   233	// the state returned by the streamSafe type.
   234	func (rb *reorderBuffer) insertUnsafe(src input, i int, info Properties) {
   235		if rune := src.hangul(i); rune != 0 {
   236			rb.decomposeHangul(rune)
   237		}
   238		if info.hasDecomposition() {
   239			// TODO: inline.
   240			rb.insertDecomposed(info.Decomposition())
   241		} else {
   242			rb.insertSingle(src, i, info)
   243		}
   244	}
   245	
   246	// insertDecomposed inserts an entry in to the reorderBuffer for each rune
   247	// in dcomp. dcomp must be a sequence of decomposed UTF-8-encoded runes.
   248	// It flushes the buffer on each new segment start.
   249	func (rb *reorderBuffer) insertDecomposed(dcomp []byte) insertErr {
   250		rb.tmpBytes.setBytes(dcomp)
   251		// As the streamSafe accounting already handles the counting for modifiers,
   252		// we don't have to call next. However, we do need to keep the accounting
   253		// intact when flushing the buffer.
   254		for i := 0; i < len(dcomp); {
   255			info := rb.f.info(rb.tmpBytes, i)
   256			if info.BoundaryBefore() && rb.nrune > 0 && !rb.doFlush() {
   257				return iShortDst
   258			}
   259			i += copy(rb.byte[rb.nbyte:], dcomp[i:i+int(info.size)])
   260			rb.insertOrdered(info)
   261		}
   262		return iSuccess
   263	}
   264	
   265	// insertSingle inserts an entry in the reorderBuffer for the rune at
   266	// position i. info is the runeInfo for the rune at position i.
   267	func (rb *reorderBuffer) insertSingle(src input, i int, info Properties) {
   268		src.copySlice(rb.byte[rb.nbyte:], i, i+int(info.size))
   269		rb.insertOrdered(info)
   270	}
   271	
   272	// insertCGJ inserts a Combining Grapheme Joiner (0x034f) into rb.
   273	func (rb *reorderBuffer) insertCGJ() {
   274		rb.insertSingle(input{str: GraphemeJoiner}, 0, Properties{size: uint8(len(GraphemeJoiner))})
   275	}
   276	
   277	// appendRune inserts a rune at the end of the buffer. It is used for Hangul.
   278	func (rb *reorderBuffer) appendRune(r rune) {
   279		bn := rb.nbyte
   280		sz := utf8.EncodeRune(rb.byte[bn:], rune(r))
   281		rb.nbyte += utf8.UTFMax
   282		rb.rune[rb.nrune] = Properties{pos: bn, size: uint8(sz)}
   283		rb.nrune++
   284	}
   285	
   286	// assignRune sets a rune at position pos. It is used for Hangul and recomposition.
   287	func (rb *reorderBuffer) assignRune(pos int, r rune) {
   288		bn := rb.rune[pos].pos
   289		sz := utf8.EncodeRune(rb.byte[bn:], rune(r))
   290		rb.rune[pos] = Properties{pos: bn, size: uint8(sz)}
   291	}
   292	
   293	// runeAt returns the rune at position n. It is used for Hangul and recomposition.
   294	func (rb *reorderBuffer) runeAt(n int) rune {
   295		inf := rb.rune[n]
   296		r, _ := utf8.DecodeRune(rb.byte[inf.pos : inf.pos+inf.size])
   297		return r
   298	}
   299	
   300	// bytesAt returns the UTF-8 encoding of the rune at position n.
   301	// It is used for Hangul and recomposition.
   302	func (rb *reorderBuffer) bytesAt(n int) []byte {
   303		inf := rb.rune[n]
   304		return rb.byte[inf.pos : int(inf.pos)+int(inf.size)]
   305	}
   306	
   307	// For Hangul we combine algorithmically, instead of using tables.
   308	const (
   309		hangulBase  = 0xAC00 // UTF-8(hangulBase) -> EA B0 80
   310		hangulBase0 = 0xEA
   311		hangulBase1 = 0xB0
   312		hangulBase2 = 0x80
   313	
   314		hangulEnd  = hangulBase + jamoLVTCount // UTF-8(0xD7A4) -> ED 9E A4
   315		hangulEnd0 = 0xED
   316		hangulEnd1 = 0x9E
   317		hangulEnd2 = 0xA4
   318	
   319		jamoLBase  = 0x1100 // UTF-8(jamoLBase) -> E1 84 00
   320		jamoLBase0 = 0xE1
   321		jamoLBase1 = 0x84
   322		jamoLEnd   = 0x1113
   323		jamoVBase  = 0x1161
   324		jamoVEnd   = 0x1176
   325		jamoTBase  = 0x11A7
   326		jamoTEnd   = 0x11C3
   327	
   328		jamoTCount   = 28
   329		jamoVCount   = 21
   330		jamoVTCount  = 21 * 28
   331		jamoLVTCount = 19 * 21 * 28
   332	)
   333	
   334	const hangulUTF8Size = 3
   335	
   336	func isHangul(b []byte) bool {
   337		if len(b) < hangulUTF8Size {
   338			return false
   339		}
   340		b0 := b[0]
   341		if b0 < hangulBase0 {
   342			return false
   343		}
   344		b1 := b[1]
   345		switch {
   346		case b0 == hangulBase0:
   347			return b1 >= hangulBase1
   348		case b0 < hangulEnd0:
   349			return true
   350		case b0 > hangulEnd0:
   351			return false
   352		case b1 < hangulEnd1:
   353			return true
   354		}
   355		return b1 == hangulEnd1 && b[2] < hangulEnd2
   356	}
   357	
   358	func isHangulString(b string) bool {
   359		if len(b) < hangulUTF8Size {
   360			return false
   361		}
   362		b0 := b[0]
   363		if b0 < hangulBase0 {
   364			return false
   365		}
   366		b1 := b[1]
   367		switch {
   368		case b0 == hangulBase0:
   369			return b1 >= hangulBase1
   370		case b0 < hangulEnd0:
   371			return true
   372		case b0 > hangulEnd0:
   373			return false
   374		case b1 < hangulEnd1:
   375			return true
   376		}
   377		return b1 == hangulEnd1 && b[2] < hangulEnd2
   378	}
   379	
   380	// Caller must ensure len(b) >= 2.
   381	func isJamoVT(b []byte) bool {
   382		// True if (rune & 0xff00) == jamoLBase
   383		return b[0] == jamoLBase0 && (b[1]&0xFC) == jamoLBase1
   384	}
   385	
   386	func isHangulWithoutJamoT(b []byte) bool {
   387		c, _ := utf8.DecodeRune(b)
   388		c -= hangulBase
   389		return c < jamoLVTCount && c%jamoTCount == 0
   390	}
   391	
   392	// decomposeHangul writes the decomposed Hangul to buf and returns the number
   393	// of bytes written.  len(buf) should be at least 9.
   394	func decomposeHangul(buf []byte, r rune) int {
   395		const JamoUTF8Len = 3
   396		r -= hangulBase
   397		x := r % jamoTCount
   398		r /= jamoTCount
   399		utf8.EncodeRune(buf, jamoLBase+r/jamoVCount)
   400		utf8.EncodeRune(buf[JamoUTF8Len:], jamoVBase+r%jamoVCount)
   401		if x != 0 {
   402			utf8.EncodeRune(buf[2*JamoUTF8Len:], jamoTBase+x)
   403			return 3 * JamoUTF8Len
   404		}
   405		return 2 * JamoUTF8Len
   406	}
   407	
   408	// decomposeHangul algorithmically decomposes a Hangul rune into
   409	// its Jamo components.
   410	// See https://unicode.org/reports/tr15/#Hangul for details on decomposing Hangul.
   411	func (rb *reorderBuffer) decomposeHangul(r rune) {
   412		r -= hangulBase
   413		x := r % jamoTCount
   414		r /= jamoTCount
   415		rb.appendRune(jamoLBase + r/jamoVCount)
   416		rb.appendRune(jamoVBase + r%jamoVCount)
   417		if x != 0 {
   418			rb.appendRune(jamoTBase + x)
   419		}
   420	}
   421	
   422	// combineHangul algorithmically combines Jamo character components into Hangul.
   423	// See https://unicode.org/reports/tr15/#Hangul for details on combining Hangul.
   424	func (rb *reorderBuffer) combineHangul(s, i, k int) {
   425		b := rb.rune[:]
   426		bn := rb.nrune
   427		for ; i < bn; i++ {
   428			cccB := b[k-1].ccc
   429			cccC := b[i].ccc
   430			if cccB == 0 {
   431				s = k - 1
   432			}
   433			if s != k-1 && cccB >= cccC {
   434				// b[i] is blocked by greater-equal cccX below it
   435				b[k] = b[i]
   436				k++
   437			} else {
   438				l := rb.runeAt(s) // also used to compare to hangulBase
   439				v := rb.runeAt(i) // also used to compare to jamoT
   440				switch {
   441				case jamoLBase <= l && l < jamoLEnd &&
   442					jamoVBase <= v && v < jamoVEnd:
   443					// 11xx plus 116x to LV
   444					rb.assignRune(s, hangulBase+
   445						(l-jamoLBase)*jamoVTCount+(v-jamoVBase)*jamoTCount)
   446				case hangulBase <= l && l < hangulEnd &&
   447					jamoTBase < v && v < jamoTEnd &&
   448					((l-hangulBase)%jamoTCount) == 0:
   449					// ACxx plus 11Ax to LVT
   450					rb.assignRune(s, l+v-jamoTBase)
   451				default:
   452					b[k] = b[i]
   453					k++
   454				}
   455			}
   456		}
   457		rb.nrune = k
   458	}
   459	
   460	// compose recombines the runes in the buffer.
   461	// It should only be used to recompose a single segment, as it will not
   462	// handle alternations between Hangul and non-Hangul characters correctly.
   463	func (rb *reorderBuffer) compose() {
   464		// Lazily load the map used by the combine func below, but do
   465		// it outside of the loop.
   466		recompMapOnce.Do(buildRecompMap)
   467	
   468		// UAX #15, section X5 , including Corrigendum #5
   469		// "In any character sequence beginning with starter S, a character C is
   470		//  blocked from S if and only if there is some character B between S
   471		//  and C, and either B is a starter or it has the same or higher
   472		//  combining class as C."
   473		bn := rb.nrune
   474		if bn == 0 {
   475			return
   476		}
   477		k := 1
   478		b := rb.rune[:]
   479		for s, i := 0, 1; i < bn; i++ {
   480			if isJamoVT(rb.bytesAt(i)) {
   481				// Redo from start in Hangul mode. Necessary to support
   482				// U+320E..U+321E in NFKC mode.
   483				rb.combineHangul(s, i, k)
   484				return
   485			}
   486			ii := b[i]
   487			// We can only use combineForward as a filter if we later
   488			// get the info for the combined character. This is more
   489			// expensive than using the filter. Using combinesBackward()
   490			// is safe.
   491			if ii.combinesBackward() {
   492				cccB := b[k-1].ccc
   493				cccC := ii.ccc
   494				blocked := false // b[i] blocked by starter or greater or equal CCC?
   495				if cccB == 0 {
   496					s = k - 1
   497				} else {
   498					blocked = s != k-1 && cccB >= cccC
   499				}
   500				if !blocked {
   501					combined := combine(rb.runeAt(s), rb.runeAt(i))
   502					if combined != 0 {
   503						rb.assignRune(s, combined)
   504						continue
   505					}
   506				}
   507			}
   508			b[k] = b[i]
   509			k++
   510		}
   511		rb.nrune = k
   512	}
   513	

View as plain text