Blame vendor/golang.org/x/text/unicode/norm/composition.go

Packit 63bb0d
// Copyright 2011 The Go Authors. All rights reserved.
Packit 63bb0d
// Use of this source code is governed by a BSD-style
Packit 63bb0d
// license that can be found in the LICENSE file.
Packit 63bb0d
Packit 63bb0d
package norm
Packit 63bb0d
Packit 63bb0d
import "unicode/utf8"
Packit 63bb0d
Packit 63bb0d
const (
Packit 63bb0d
	maxNonStarters = 30
Packit 63bb0d
	// The maximum number of characters needed for a buffer is
Packit 63bb0d
	// maxNonStarters + 1 for the starter + 1 for the GCJ
Packit 63bb0d
	maxBufferSize    = maxNonStarters + 2
Packit 63bb0d
	maxNFCExpansion  = 3  // NFC(0x1D160)
Packit 63bb0d
	maxNFKCExpansion = 18 // NFKC(0xFDFA)
Packit 63bb0d
Packit 63bb0d
	maxByteBufferSize = utf8.UTFMax * maxBufferSize // 128
Packit 63bb0d
)
Packit 63bb0d
Packit 63bb0d
// ssState is used for reporting the segment state after inserting a rune.
Packit 63bb0d
// It is returned by streamSafe.next.
Packit 63bb0d
type ssState int
Packit 63bb0d
Packit 63bb0d
const (
Packit 63bb0d
	// Indicates a rune was successfully added to the segment.
Packit 63bb0d
	ssSuccess ssState = iota
Packit 63bb0d
	// Indicates a rune starts a new segment and should not be added.
Packit 63bb0d
	ssStarter
Packit 63bb0d
	// Indicates a rune caused a segment overflow and a CGJ should be inserted.
Packit 63bb0d
	ssOverflow
Packit 63bb0d
)
Packit 63bb0d
Packit 63bb0d
// streamSafe implements the policy of when a CGJ should be inserted.
Packit 63bb0d
type streamSafe uint8
Packit 63bb0d
Packit 63bb0d
// first inserts the first rune of a segment. It is a faster version of next if
Packit 63bb0d
// it is known p represents the first rune in a segment.
Packit 63bb0d
func (ss *streamSafe) first(p Properties) {
Packit 63bb0d
	*ss = streamSafe(p.nTrailingNonStarters())
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// insert returns a ssState value to indicate whether a rune represented by p
Packit 63bb0d
// can be inserted.
Packit 63bb0d
func (ss *streamSafe) next(p Properties) ssState {
Packit 63bb0d
	if *ss > maxNonStarters {
Packit 63bb0d
		panic("streamSafe was not reset")
Packit 63bb0d
	}
Packit 63bb0d
	n := p.nLeadingNonStarters()
Packit 63bb0d
	if *ss += streamSafe(n); *ss > maxNonStarters {
Packit 63bb0d
		*ss = 0
Packit 63bb0d
		return ssOverflow
Packit 63bb0d
	}
Packit 63bb0d
	// The Stream-Safe Text Processing prescribes that the counting can stop
Packit 63bb0d
	// as soon as a starter is encountered. However, there are some starters,
Packit 63bb0d
	// like Jamo V and T, that can combine with other runes, leaving their
Packit 63bb0d
	// successive non-starters appended to the previous, possibly causing an
Packit 63bb0d
	// overflow. We will therefore consider any rune with a non-zero nLead to
Packit 63bb0d
	// be a non-starter. Note that it always hold that if nLead > 0 then
Packit 63bb0d
	// nLead == nTrail.
Packit 63bb0d
	if n == 0 {
Packit 63bb0d
		*ss = streamSafe(p.nTrailingNonStarters())
Packit 63bb0d
		return ssStarter
Packit 63bb0d
	}
Packit 63bb0d
	return ssSuccess
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// backwards is used for checking for overflow and segment starts
Packit 63bb0d
// when traversing a string backwards. Users do not need to call first
Packit 63bb0d
// for the first rune. The state of the streamSafe retains the count of
Packit 63bb0d
// the non-starters loaded.
Packit 63bb0d
func (ss *streamSafe) backwards(p Properties) ssState {
Packit 63bb0d
	if *ss > maxNonStarters {
Packit 63bb0d
		panic("streamSafe was not reset")
Packit 63bb0d
	}
Packit 63bb0d
	c := *ss + streamSafe(p.nTrailingNonStarters())
Packit 63bb0d
	if c > maxNonStarters {
Packit 63bb0d
		return ssOverflow
Packit 63bb0d
	}
Packit 63bb0d
	*ss = c
Packit 63bb0d
	if p.nLeadingNonStarters() == 0 {
Packit 63bb0d
		return ssStarter
Packit 63bb0d
	}
Packit 63bb0d
	return ssSuccess
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (ss streamSafe) isMax() bool {
Packit 63bb0d
	return ss == maxNonStarters
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// GraphemeJoiner is inserted after maxNonStarters non-starter runes.
Packit 63bb0d
const GraphemeJoiner = "\u034F"
Packit 63bb0d
Packit 63bb0d
// reorderBuffer is used to normalize a single segment.  Characters inserted with
Packit 63bb0d
// insert are decomposed and reordered based on CCC. The compose method can
Packit 63bb0d
// be used to recombine characters.  Note that the byte buffer does not hold
Packit 63bb0d
// the UTF-8 characters in order.  Only the rune array is maintained in sorted
Packit 63bb0d
// order. flush writes the resulting segment to a byte array.
Packit 63bb0d
type reorderBuffer struct {
Packit 63bb0d
	rune  [maxBufferSize]Properties // Per character info.
Packit 63bb0d
	byte  [maxByteBufferSize]byte   // UTF-8 buffer. Referenced by runeInfo.pos.
Packit 63bb0d
	nbyte uint8                     // Number or bytes.
Packit 63bb0d
	ss    streamSafe                // For limiting length of non-starter sequence.
Packit 63bb0d
	nrune int                       // Number of runeInfos.
Packit 63bb0d
	f     formInfo
Packit 63bb0d
Packit 63bb0d
	src      input
Packit 63bb0d
	nsrc     int
Packit 63bb0d
	tmpBytes input
Packit 63bb0d
Packit 63bb0d
	out    []byte
Packit 63bb0d
	flushF func(*reorderBuffer) bool
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (rb *reorderBuffer) init(f Form, src []byte) {
Packit 63bb0d
	rb.f = *formTable[f]
Packit 63bb0d
	rb.src.setBytes(src)
Packit 63bb0d
	rb.nsrc = len(src)
Packit 63bb0d
	rb.ss = 0
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (rb *reorderBuffer) initString(f Form, src string) {
Packit 63bb0d
	rb.f = *formTable[f]
Packit 63bb0d
	rb.src.setString(src)
Packit 63bb0d
	rb.nsrc = len(src)
Packit 63bb0d
	rb.ss = 0
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (rb *reorderBuffer) setFlusher(out []byte, f func(*reorderBuffer) bool) {
Packit 63bb0d
	rb.out = out
Packit 63bb0d
	rb.flushF = f
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// reset discards all characters from the buffer.
Packit 63bb0d
func (rb *reorderBuffer) reset() {
Packit 63bb0d
	rb.nrune = 0
Packit 63bb0d
	rb.nbyte = 0
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (rb *reorderBuffer) doFlush() bool {
Packit 63bb0d
	if rb.f.composing {
Packit 63bb0d
		rb.compose()
Packit 63bb0d
	}
Packit 63bb0d
	res := rb.flushF(rb)
Packit 63bb0d
	rb.reset()
Packit 63bb0d
	return res
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// appendFlush appends the normalized segment to rb.out.
Packit 63bb0d
func appendFlush(rb *reorderBuffer) bool {
Packit 63bb0d
	for i := 0; i < rb.nrune; i++ {
Packit 63bb0d
		start := rb.rune[i].pos
Packit 63bb0d
		end := start + rb.rune[i].size
Packit 63bb0d
		rb.out = append(rb.out, rb.byte[start:end]...)
Packit 63bb0d
	}
Packit 63bb0d
	return true
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// flush appends the normalized segment to out and resets rb.
Packit 63bb0d
func (rb *reorderBuffer) flush(out []byte) []byte {
Packit 63bb0d
	for i := 0; i < rb.nrune; i++ {
Packit 63bb0d
		start := rb.rune[i].pos
Packit 63bb0d
		end := start + rb.rune[i].size
Packit 63bb0d
		out = append(out, rb.byte[start:end]...)
Packit 63bb0d
	}
Packit 63bb0d
	rb.reset()
Packit 63bb0d
	return out
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// flushCopy copies the normalized segment to buf and resets rb.
Packit 63bb0d
// It returns the number of bytes written to buf.
Packit 63bb0d
func (rb *reorderBuffer) flushCopy(buf []byte) int {
Packit 63bb0d
	p := 0
Packit 63bb0d
	for i := 0; i < rb.nrune; i++ {
Packit 63bb0d
		runep := rb.rune[i]
Packit 63bb0d
		p += copy(buf[p:], rb.byte[runep.pos:runep.pos+runep.size])
Packit 63bb0d
	}
Packit 63bb0d
	rb.reset()
Packit 63bb0d
	return p
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// insertOrdered inserts a rune in the buffer, ordered by Canonical Combining Class.
Packit 63bb0d
// It returns false if the buffer is not large enough to hold the rune.
Packit 63bb0d
// It is used internally by insert and insertString only.
Packit 63bb0d
func (rb *reorderBuffer) insertOrdered(info Properties) {
Packit 63bb0d
	n := rb.nrune
Packit 63bb0d
	b := rb.rune[:]
Packit 63bb0d
	cc := info.ccc
Packit 63bb0d
	if cc > 0 {
Packit 63bb0d
		// Find insertion position + move elements to make room.
Packit 63bb0d
		for ; n > 0; n-- {
Packit 63bb0d
			if b[n-1].ccc <= cc {
Packit 63bb0d
				break
Packit 63bb0d
			}
Packit 63bb0d
			b[n] = b[n-1]
Packit 63bb0d
		}
Packit 63bb0d
	}
Packit 63bb0d
	rb.nrune += 1
Packit 63bb0d
	pos := uint8(rb.nbyte)
Packit 63bb0d
	rb.nbyte += utf8.UTFMax
Packit 63bb0d
	info.pos = pos
Packit 63bb0d
	b[n] = info
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// insertErr is an error code returned by insert. Using this type instead
Packit 63bb0d
// of error improves performance up to 20% for many of the benchmarks.
Packit 63bb0d
type insertErr int
Packit 63bb0d
Packit 63bb0d
const (
Packit 63bb0d
	iSuccess insertErr = -iota
Packit 63bb0d
	iShortDst
Packit 63bb0d
	iShortSrc
Packit 63bb0d
)
Packit 63bb0d
Packit 63bb0d
// insertFlush inserts the given rune in the buffer ordered by CCC.
Packit 63bb0d
// If a decomposition with multiple segments are encountered, they leading
Packit 63bb0d
// ones are flushed.
Packit 63bb0d
// It returns a non-zero error code if the rune was not inserted.
Packit 63bb0d
func (rb *reorderBuffer) insertFlush(src input, i int, info Properties) insertErr {
Packit 63bb0d
	if rune := src.hangul(i); rune != 0 {
Packit 63bb0d
		rb.decomposeHangul(rune)
Packit 63bb0d
		return iSuccess
Packit 63bb0d
	}
Packit 63bb0d
	if info.hasDecomposition() {
Packit 63bb0d
		return rb.insertDecomposed(info.Decomposition())
Packit 63bb0d
	}
Packit 63bb0d
	rb.insertSingle(src, i, info)
Packit 63bb0d
	return iSuccess
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// insertUnsafe inserts the given rune in the buffer ordered by CCC.
Packit 63bb0d
// It is assumed there is sufficient space to hold the runes. It is the
Packit 63bb0d
// responsibility of the caller to ensure this. This can be done by checking
Packit 63bb0d
// the state returned by the streamSafe type.
Packit 63bb0d
func (rb *reorderBuffer) insertUnsafe(src input, i int, info Properties) {
Packit 63bb0d
	if rune := src.hangul(i); rune != 0 {
Packit 63bb0d
		rb.decomposeHangul(rune)
Packit 63bb0d
	}
Packit 63bb0d
	if info.hasDecomposition() {
Packit 63bb0d
		// TODO: inline.
Packit 63bb0d
		rb.insertDecomposed(info.Decomposition())
Packit 63bb0d
	} else {
Packit 63bb0d
		rb.insertSingle(src, i, info)
Packit 63bb0d
	}
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// insertDecomposed inserts an entry in to the reorderBuffer for each rune
Packit 63bb0d
// in dcomp. dcomp must be a sequence of decomposed UTF-8-encoded runes.
Packit 63bb0d
// It flushes the buffer on each new segment start.
Packit 63bb0d
func (rb *reorderBuffer) insertDecomposed(dcomp []byte) insertErr {
Packit 63bb0d
	rb.tmpBytes.setBytes(dcomp)
Packit 63bb0d
	// As the streamSafe accounting already handles the counting for modifiers,
Packit 63bb0d
	// we don't have to call next. However, we do need to keep the accounting
Packit 63bb0d
	// intact when flushing the buffer.
Packit 63bb0d
	for i := 0; i < len(dcomp); {
Packit 63bb0d
		info := rb.f.info(rb.tmpBytes, i)
Packit 63bb0d
		if info.BoundaryBefore() && rb.nrune > 0 && !rb.doFlush() {
Packit 63bb0d
			return iShortDst
Packit 63bb0d
		}
Packit 63bb0d
		i += copy(rb.byte[rb.nbyte:], dcomp[i:i+int(info.size)])
Packit 63bb0d
		rb.insertOrdered(info)
Packit 63bb0d
	}
Packit 63bb0d
	return iSuccess
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// insertSingle inserts an entry in the reorderBuffer for the rune at
Packit 63bb0d
// position i. info is the runeInfo for the rune at position i.
Packit 63bb0d
func (rb *reorderBuffer) insertSingle(src input, i int, info Properties) {
Packit 63bb0d
	src.copySlice(rb.byte[rb.nbyte:], i, i+int(info.size))
Packit 63bb0d
	rb.insertOrdered(info)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// insertCGJ inserts a Combining Grapheme Joiner (0x034f) into rb.
Packit 63bb0d
func (rb *reorderBuffer) insertCGJ() {
Packit 63bb0d
	rb.insertSingle(input{str: GraphemeJoiner}, 0, Properties{size: uint8(len(GraphemeJoiner))})
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// appendRune inserts a rune at the end of the buffer. It is used for Hangul.
Packit 63bb0d
func (rb *reorderBuffer) appendRune(r rune) {
Packit 63bb0d
	bn := rb.nbyte
Packit 63bb0d
	sz := utf8.EncodeRune(rb.byte[bn:], rune(r))
Packit 63bb0d
	rb.nbyte += utf8.UTFMax
Packit 63bb0d
	rb.rune[rb.nrune] = Properties{pos: bn, size: uint8(sz)}
Packit 63bb0d
	rb.nrune++
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// assignRune sets a rune at position pos. It is used for Hangul and recomposition.
Packit 63bb0d
func (rb *reorderBuffer) assignRune(pos int, r rune) {
Packit 63bb0d
	bn := rb.rune[pos].pos
Packit 63bb0d
	sz := utf8.EncodeRune(rb.byte[bn:], rune(r))
Packit 63bb0d
	rb.rune[pos] = Properties{pos: bn, size: uint8(sz)}
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// runeAt returns the rune at position n. It is used for Hangul and recomposition.
Packit 63bb0d
func (rb *reorderBuffer) runeAt(n int) rune {
Packit 63bb0d
	inf := rb.rune[n]
Packit 63bb0d
	r, _ := utf8.DecodeRune(rb.byte[inf.pos : inf.pos+inf.size])
Packit 63bb0d
	return r
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// bytesAt returns the UTF-8 encoding of the rune at position n.
Packit 63bb0d
// It is used for Hangul and recomposition.
Packit 63bb0d
func (rb *reorderBuffer) bytesAt(n int) []byte {
Packit 63bb0d
	inf := rb.rune[n]
Packit 63bb0d
	return rb.byte[inf.pos : int(inf.pos)+int(inf.size)]
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// For Hangul we combine algorithmically, instead of using tables.
Packit 63bb0d
const (
Packit 63bb0d
	hangulBase  = 0xAC00 // UTF-8(hangulBase) -> EA B0 80
Packit 63bb0d
	hangulBase0 = 0xEA
Packit 63bb0d
	hangulBase1 = 0xB0
Packit 63bb0d
	hangulBase2 = 0x80
Packit 63bb0d
Packit 63bb0d
	hangulEnd  = hangulBase + jamoLVTCount // UTF-8(0xD7A4) -> ED 9E A4
Packit 63bb0d
	hangulEnd0 = 0xED
Packit 63bb0d
	hangulEnd1 = 0x9E
Packit 63bb0d
	hangulEnd2 = 0xA4
Packit 63bb0d
Packit 63bb0d
	jamoLBase  = 0x1100 // UTF-8(jamoLBase) -> E1 84 00
Packit 63bb0d
	jamoLBase0 = 0xE1
Packit 63bb0d
	jamoLBase1 = 0x84
Packit 63bb0d
	jamoLEnd   = 0x1113
Packit 63bb0d
	jamoVBase  = 0x1161
Packit 63bb0d
	jamoVEnd   = 0x1176
Packit 63bb0d
	jamoTBase  = 0x11A7
Packit 63bb0d
	jamoTEnd   = 0x11C3
Packit 63bb0d
Packit 63bb0d
	jamoTCount   = 28
Packit 63bb0d
	jamoVCount   = 21
Packit 63bb0d
	jamoVTCount  = 21 * 28
Packit 63bb0d
	jamoLVTCount = 19 * 21 * 28
Packit 63bb0d
)
Packit 63bb0d
Packit 63bb0d
const hangulUTF8Size = 3
Packit 63bb0d
Packit 63bb0d
func isHangul(b []byte) bool {
Packit 63bb0d
	if len(b) < hangulUTF8Size {
Packit 63bb0d
		return false
Packit 63bb0d
	}
Packit 63bb0d
	b0 := b[0]
Packit 63bb0d
	if b0 < hangulBase0 {
Packit 63bb0d
		return false
Packit 63bb0d
	}
Packit 63bb0d
	b1 := b[1]
Packit 63bb0d
	switch {
Packit 63bb0d
	case b0 == hangulBase0:
Packit 63bb0d
		return b1 >= hangulBase1
Packit 63bb0d
	case b0 < hangulEnd0:
Packit 63bb0d
		return true
Packit 63bb0d
	case b0 > hangulEnd0:
Packit 63bb0d
		return false
Packit 63bb0d
	case b1 < hangulEnd1:
Packit 63bb0d
		return true
Packit 63bb0d
	}
Packit 63bb0d
	return b1 == hangulEnd1 && b[2] < hangulEnd2
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func isHangulString(b string) bool {
Packit 63bb0d
	if len(b) < hangulUTF8Size {
Packit 63bb0d
		return false
Packit 63bb0d
	}
Packit 63bb0d
	b0 := b[0]
Packit 63bb0d
	if b0 < hangulBase0 {
Packit 63bb0d
		return false
Packit 63bb0d
	}
Packit 63bb0d
	b1 := b[1]
Packit 63bb0d
	switch {
Packit 63bb0d
	case b0 == hangulBase0:
Packit 63bb0d
		return b1 >= hangulBase1
Packit 63bb0d
	case b0 < hangulEnd0:
Packit 63bb0d
		return true
Packit 63bb0d
	case b0 > hangulEnd0:
Packit 63bb0d
		return false
Packit 63bb0d
	case b1 < hangulEnd1:
Packit 63bb0d
		return true
Packit 63bb0d
	}
Packit 63bb0d
	return b1 == hangulEnd1 && b[2] < hangulEnd2
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// Caller must ensure len(b) >= 2.
Packit 63bb0d
func isJamoVT(b []byte) bool {
Packit 63bb0d
	// True if (rune & 0xff00) == jamoLBase
Packit 63bb0d
	return b[0] == jamoLBase0 && (b[1]&0xFC) == jamoLBase1
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func isHangulWithoutJamoT(b []byte) bool {
Packit 63bb0d
	c, _ := utf8.DecodeRune(b)
Packit 63bb0d
	c -= hangulBase
Packit 63bb0d
	return c < jamoLVTCount && c%jamoTCount == 0
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// decomposeHangul writes the decomposed Hangul to buf and returns the number
Packit 63bb0d
// of bytes written.  len(buf) should be at least 9.
Packit 63bb0d
func decomposeHangul(buf []byte, r rune) int {
Packit 63bb0d
	const JamoUTF8Len = 3
Packit 63bb0d
	r -= hangulBase
Packit 63bb0d
	x := r % jamoTCount
Packit 63bb0d
	r /= jamoTCount
Packit 63bb0d
	utf8.EncodeRune(buf, jamoLBase+r/jamoVCount)
Packit 63bb0d
	utf8.EncodeRune(buf[JamoUTF8Len:], jamoVBase+r%jamoVCount)
Packit 63bb0d
	if x != 0 {
Packit 63bb0d
		utf8.EncodeRune(buf[2*JamoUTF8Len:], jamoTBase+x)
Packit 63bb0d
		return 3 * JamoUTF8Len
Packit 63bb0d
	}
Packit 63bb0d
	return 2 * JamoUTF8Len
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// decomposeHangul algorithmically decomposes a Hangul rune into
Packit 63bb0d
// its Jamo components.
Packit 63bb0d
// See https://unicode.org/reports/tr15/#Hangul for details on decomposing Hangul.
Packit 63bb0d
func (rb *reorderBuffer) decomposeHangul(r rune) {
Packit 63bb0d
	r -= hangulBase
Packit 63bb0d
	x := r % jamoTCount
Packit 63bb0d
	r /= jamoTCount
Packit 63bb0d
	rb.appendRune(jamoLBase + r/jamoVCount)
Packit 63bb0d
	rb.appendRune(jamoVBase + r%jamoVCount)
Packit 63bb0d
	if x != 0 {
Packit 63bb0d
		rb.appendRune(jamoTBase + x)
Packit 63bb0d
	}
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// combineHangul algorithmically combines Jamo character components into Hangul.
Packit 63bb0d
// See https://unicode.org/reports/tr15/#Hangul for details on combining Hangul.
Packit 63bb0d
func (rb *reorderBuffer) combineHangul(s, i, k int) {
Packit 63bb0d
	b := rb.rune[:]
Packit 63bb0d
	bn := rb.nrune
Packit 63bb0d
	for ; i < bn; i++ {
Packit 63bb0d
		cccB := b[k-1].ccc
Packit 63bb0d
		cccC := b[i].ccc
Packit 63bb0d
		if cccB == 0 {
Packit 63bb0d
			s = k - 1
Packit 63bb0d
		}
Packit 63bb0d
		if s != k-1 && cccB >= cccC {
Packit 63bb0d
			// b[i] is blocked by greater-equal cccX below it
Packit 63bb0d
			b[k] = b[i]
Packit 63bb0d
			k++
Packit 63bb0d
		} else {
Packit 63bb0d
			l := rb.runeAt(s) // also used to compare to hangulBase
Packit 63bb0d
			v := rb.runeAt(i) // also used to compare to jamoT
Packit 63bb0d
			switch {
Packit 63bb0d
			case jamoLBase <= l && l < jamoLEnd &&
Packit 63bb0d
				jamoVBase <= v && v < jamoVEnd:
Packit 63bb0d
				// 11xx plus 116x to LV
Packit 63bb0d
				rb.assignRune(s, hangulBase+
Packit 63bb0d
					(l-jamoLBase)*jamoVTCount+(v-jamoVBase)*jamoTCount)
Packit 63bb0d
			case hangulBase <= l && l < hangulEnd &&
Packit 63bb0d
				jamoTBase < v && v < jamoTEnd &&
Packit 63bb0d
				((l-hangulBase)%jamoTCount) == 0:
Packit 63bb0d
				// ACxx plus 11Ax to LVT
Packit 63bb0d
				rb.assignRune(s, l+v-jamoTBase)
Packit 63bb0d
			default:
Packit 63bb0d
				b[k] = b[i]
Packit 63bb0d
				k++
Packit 63bb0d
			}
Packit 63bb0d
		}
Packit 63bb0d
	}
Packit 63bb0d
	rb.nrune = k
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// compose recombines the runes in the buffer.
Packit 63bb0d
// It should only be used to recompose a single segment, as it will not
Packit 63bb0d
// handle alternations between Hangul and non-Hangul characters correctly.
Packit 63bb0d
func (rb *reorderBuffer) compose() {
Packit 63bb0d
	// Lazily load the map used by the combine func below, but do
Packit 63bb0d
	// it outside of the loop.
Packit 63bb0d
	recompMapOnce.Do(buildRecompMap)
Packit 63bb0d
Packit 63bb0d
	// UAX #15, section X5 , including Corrigendum #5
Packit 63bb0d
	// "In any character sequence beginning with starter S, a character C is
Packit 63bb0d
	//  blocked from S if and only if there is some character B between S
Packit 63bb0d
	//  and C, and either B is a starter or it has the same or higher
Packit 63bb0d
	//  combining class as C."
Packit 63bb0d
	bn := rb.nrune
Packit 63bb0d
	if bn == 0 {
Packit 63bb0d
		return
Packit 63bb0d
	}
Packit 63bb0d
	k := 1
Packit 63bb0d
	b := rb.rune[:]
Packit 63bb0d
	for s, i := 0, 1; i < bn; i++ {
Packit 63bb0d
		if isJamoVT(rb.bytesAt(i)) {
Packit 63bb0d
			// Redo from start in Hangul mode. Necessary to support
Packit 63bb0d
			// U+320E..U+321E in NFKC mode.
Packit 63bb0d
			rb.combineHangul(s, i, k)
Packit 63bb0d
			return
Packit 63bb0d
		}
Packit 63bb0d
		ii := b[i]
Packit 63bb0d
		// We can only use combineForward as a filter if we later
Packit 63bb0d
		// get the info for the combined character. This is more
Packit 63bb0d
		// expensive than using the filter. Using combinesBackward()
Packit 63bb0d
		// is safe.
Packit 63bb0d
		if ii.combinesBackward() {
Packit 63bb0d
			cccB := b[k-1].ccc
Packit 63bb0d
			cccC := ii.ccc
Packit 63bb0d
			blocked := false // b[i] blocked by starter or greater or equal CCC?
Packit 63bb0d
			if cccB == 0 {
Packit 63bb0d
				s = k - 1
Packit 63bb0d
			} else {
Packit 63bb0d
				blocked = s != k-1 && cccB >= cccC
Packit 63bb0d
			}
Packit 63bb0d
			if !blocked {
Packit 63bb0d
				combined := combine(rb.runeAt(s), rb.runeAt(i))
Packit 63bb0d
				if combined != 0 {
Packit 63bb0d
					rb.assignRune(s, combined)
Packit 63bb0d
					continue
Packit 63bb0d
				}
Packit 63bb0d
			}
Packit 63bb0d
		}
Packit 63bb0d
		b[k] = b[i]
Packit 63bb0d
		k++
Packit 63bb0d
	}
Packit 63bb0d
	rb.nrune = k
Packit 63bb0d
}