1
2
3
4
5 package norm
6
7 import "unicode/utf8"
8
9 const (
10 maxNonStarters = 30
11
12
13 maxBufferSize = maxNonStarters + 2
14 maxNFCExpansion = 3
15 maxNFKCExpansion = 18
16
17 maxByteBufferSize = utf8.UTFMax * maxBufferSize
18 )
19
20
21
22 type ssState int
23
24 const (
25
26 ssSuccess ssState = iota
27
28 ssStarter
29
30 ssOverflow
31 )
32
33
34 type streamSafe uint8
35
36
37
38 func (ss *streamSafe) first(p Properties) {
39 *ss = streamSafe(p.nTrailingNonStarters())
40 }
41
42
43
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
54
55
56
57
58
59
60 if n == 0 {
61 *ss = streamSafe(p.nTrailingNonStarters())
62 return ssStarter
63 }
64 return ssSuccess
65 }
66
67
68
69
70
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
91 const GraphemeJoiner = "\u034F"
92
93
94
95
96
97
98 type reorderBuffer struct {
99 rune [maxBufferSize]Properties
100 byte [maxByteBufferSize]byte
101 nbyte uint8
102 ss streamSafe
103 nrune int
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
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
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
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
170
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
182
183
184 func (rb *reorderBuffer) insertOrdered(info Properties) {
185 n := rb.nrune
186 b := rb.rune[:]
187 cc := info.ccc
188 if cc > 0 {
189
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
205
206 type insertErr int
207
208 const (
209 iSuccess insertErr = -iota
210 iShortDst
211 iShortSrc
212 )
213
214
215
216
217
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
231
232
233
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
240 rb.insertDecomposed(info.Decomposition())
241 } else {
242 rb.insertSingle(src, i, info)
243 }
244 }
245
246
247
248
249 func (rb *reorderBuffer) insertDecomposed(dcomp []byte) insertErr {
250 rb.tmpBytes.setBytes(dcomp)
251
252
253
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
266
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
273 func (rb *reorderBuffer) insertCGJ() {
274 rb.insertSingle(input{str: GraphemeJoiner}, 0, Properties{size: uint8(len(GraphemeJoiner))})
275 }
276
277
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
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
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
301
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
308 const (
309 hangulBase = 0xAC00
310 hangulBase0 = 0xEA
311 hangulBase1 = 0xB0
312 hangulBase2 = 0x80
313
314 hangulEnd = hangulBase + jamoLVTCount
315 hangulEnd0 = 0xED
316 hangulEnd1 = 0x9E
317 hangulEnd2 = 0xA4
318
319 jamoLBase = 0x1100
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
381 func isJamoVT(b []byte) bool {
382
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
393
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
409
410
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
423
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
435 b[k] = b[i]
436 k++
437 } else {
438 l := rb.runeAt(s)
439 v := rb.runeAt(i)
440 switch {
441 case jamoLBase <= l && l < jamoLEnd &&
442 jamoVBase <= v && v < jamoVEnd:
443
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
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
461
462
463 func (rb *reorderBuffer) compose() {
464
465
466 recompMapOnce.Do(buildRecompMap)
467
468
469
470
471
472
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
482
483 rb.combineHangul(s, i, k)
484 return
485 }
486 ii := b[i]
487
488
489
490
491 if ii.combinesBackward() {
492 cccB := b[k-1].ccc
493 cccC := ii.ccc
494 blocked := false
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