1
2 package util
3
4 import (
5 "bytes"
6 "io"
7 "net/url"
8 "regexp"
9 "sort"
10 "strconv"
11 "unicode"
12 "unicode/utf8"
13 )
14
15
16
17 type CopyOnWriteBuffer struct {
18 buffer []byte
19 copied bool
20 }
21
22
23 func NewCopyOnWriteBuffer(buffer []byte) CopyOnWriteBuffer {
24 return CopyOnWriteBuffer{
25 buffer: buffer,
26 copied: false,
27 }
28 }
29
30
31
32 func (b *CopyOnWriteBuffer) Write(value []byte) {
33 if !b.copied {
34 b.buffer = make([]byte, 0, len(b.buffer)+20)
35 b.copied = true
36 }
37 b.buffer = append(b.buffer, value...)
38 }
39
40
41
42 func (b *CopyOnWriteBuffer) WriteString(value string) {
43 b.Write(StringToReadOnlyBytes(value))
44 }
45
46
47
48 func (b *CopyOnWriteBuffer) Append(value []byte) {
49 if !b.copied {
50 tmp := make([]byte, len(b.buffer), len(b.buffer)+20)
51 copy(tmp, b.buffer)
52 b.buffer = tmp
53 b.copied = true
54 }
55 b.buffer = append(b.buffer, value...)
56 }
57
58
59
60 func (b *CopyOnWriteBuffer) AppendString(value string) {
61 b.Append(StringToReadOnlyBytes(value))
62 }
63
64
65
66 func (b *CopyOnWriteBuffer) WriteByte(c byte) error {
67 if !b.copied {
68 b.buffer = make([]byte, 0, len(b.buffer)+20)
69 b.copied = true
70 }
71 b.buffer = append(b.buffer, c)
72 return nil
73 }
74
75
76
77 func (b *CopyOnWriteBuffer) AppendByte(c byte) {
78 if !b.copied {
79 tmp := make([]byte, len(b.buffer), len(b.buffer)+20)
80 copy(tmp, b.buffer)
81 b.buffer = tmp
82 b.copied = true
83 }
84 b.buffer = append(b.buffer, c)
85 }
86
87
88 func (b *CopyOnWriteBuffer) Bytes() []byte {
89 return b.buffer
90 }
91
92
93 func (b *CopyOnWriteBuffer) IsCopied() bool {
94 return b.copied
95 }
96
97
98
99 func IsEscapedPunctuation(source []byte, i int) bool {
100 return source[i] == '\\' && i < len(source)-1 && IsPunct(source[i+1])
101 }
102
103
104 func ReadWhile(source []byte, index [2]int, pred func(byte) bool) (int, bool) {
105 j := index[0]
106 ok := false
107 for ; j < index[1]; j++ {
108 c1 := source[j]
109 if pred(c1) {
110 ok = true
111 continue
112 }
113 break
114 }
115 return j, ok
116 }
117
118
119 func IsBlank(bs []byte) bool {
120 for _, b := range bs {
121 if !IsSpace(b) {
122 return false
123 }
124 }
125 return true
126 }
127
128
129 func VisualizeSpaces(bs []byte) []byte {
130 bs = bytes.Replace(bs, []byte(" "), []byte("[SPACE]"), -1)
131 bs = bytes.Replace(bs, []byte("\t"), []byte("[TAB]"), -1)
132 bs = bytes.Replace(bs, []byte("\n"), []byte("[NEWLINE]\n"), -1)
133 bs = bytes.Replace(bs, []byte("\r"), []byte("[CR]"), -1)
134 bs = bytes.Replace(bs, []byte("\v"), []byte("[VTAB]"), -1)
135 bs = bytes.Replace(bs, []byte("\x00"), []byte("[NUL]"), -1)
136 bs = bytes.Replace(bs, []byte("\ufffd"), []byte("[U+FFFD]"), -1)
137 return bs
138 }
139
140
141 func TabWidth(currentPos int) int {
142 return 4 - currentPos%4
143 }
144
145
146
147
148
149
150
151
152
153
154
155 func IndentPosition(bs []byte, currentPos, width int) (pos, padding int) {
156 return IndentPositionPadding(bs, currentPos, 0, width)
157 }
158
159
160
161
162 func IndentPositionPadding(bs []byte, currentPos, paddingv, width int) (pos, padding int) {
163 if width == 0 {
164 return 0, paddingv
165 }
166 w := 0
167 i := 0
168 l := len(bs)
169 for ; i < l; i++ {
170 if bs[i] == '\t' && w < width {
171 w += TabWidth(currentPos + w)
172 } else if bs[i] == ' ' && w < width {
173 w++
174 } else {
175 break
176 }
177 }
178 if w >= width {
179 return i - paddingv, w - width
180 }
181 return -1, -1
182 }
183
184
185
186
187 func DedentPosition(bs []byte, currentPos, width int) (pos, padding int) {
188 if width == 0 {
189 return 0, 0
190 }
191 w := 0
192 l := len(bs)
193 i := 0
194 for ; i < l; i++ {
195 if bs[i] == '\t' {
196 w += TabWidth(currentPos + w)
197 } else if bs[i] == ' ' {
198 w++
199 } else {
200 break
201 }
202 }
203 if w >= width {
204 return i, w - width
205 }
206 return i, 0
207 }
208
209
210
211
212
213
214 func DedentPositionPadding(bs []byte, currentPos, paddingv, width int) (pos, padding int) {
215 if width == 0 {
216 return 0, paddingv
217 }
218
219 w := 0
220 i := 0
221 l := len(bs)
222 for ; i < l; i++ {
223 if bs[i] == '\t' {
224 w += TabWidth(currentPos + w)
225 } else if bs[i] == ' ' {
226 w++
227 } else {
228 break
229 }
230 }
231 if w >= width {
232 return i - paddingv, w - width
233 }
234 return i - paddingv, 0
235 }
236
237
238 func IndentWidth(bs []byte, currentPos int) (width, pos int) {
239 l := len(bs)
240 for i := 0; i < l; i++ {
241 b := bs[i]
242 if b == ' ' {
243 width++
244 pos++
245 } else if b == '\t' {
246 width += TabWidth(currentPos + width)
247 pos++
248 } else {
249 break
250 }
251 }
252 return
253 }
254
255
256
257 func FirstNonSpacePosition(bs []byte) int {
258 i := 0
259 for ; i < len(bs); i++ {
260 c := bs[i]
261 if c == ' ' || c == '\t' {
262 continue
263 }
264 if c == '\n' {
265 return -1
266 }
267 return i
268 }
269 return -1
270 }
271
272
273
274
275
276
277
278
279
280 func FindClosure(bs []byte, opener, closure byte, codeSpan, allowNesting bool) int {
281 i := 0
282 opened := 1
283 codeSpanOpener := 0
284 for i < len(bs) {
285 c := bs[i]
286 if codeSpan && codeSpanOpener != 0 && c == '`' {
287 codeSpanCloser := 0
288 for ; i < len(bs); i++ {
289 if bs[i] == '`' {
290 codeSpanCloser++
291 } else {
292 i--
293 break
294 }
295 }
296 if codeSpanCloser == codeSpanOpener {
297 codeSpanOpener = 0
298 }
299 } else if codeSpanOpener == 0 && c == '\\' && i < len(bs)-1 && IsPunct(bs[i+1]) {
300 i += 2
301 continue
302 } else if codeSpan && codeSpanOpener == 0 && c == '`' {
303 for ; i < len(bs); i++ {
304 if bs[i] == '`' {
305 codeSpanOpener++
306 } else {
307 i--
308 break
309 }
310 }
311 } else if (codeSpan && codeSpanOpener == 0) || !codeSpan {
312 if c == closure {
313 opened--
314 if opened == 0 {
315 return i
316 }
317 } else if c == opener {
318 if !allowNesting {
319 return -1
320 }
321 opened++
322 }
323 }
324 i++
325 }
326 return -1
327 }
328
329
330
331
332 func TrimLeft(source, b []byte) []byte {
333 i := 0
334 for ; i < len(source); i++ {
335 c := source[i]
336 found := false
337 for j := 0; j < len(b); j++ {
338 if c == b[j] {
339 found = true
340 break
341 }
342 }
343 if !found {
344 break
345 }
346 }
347 return source[i:]
348 }
349
350
351 func TrimRight(source, b []byte) []byte {
352 i := len(source) - 1
353 for ; i >= 0; i-- {
354 c := source[i]
355 found := false
356 for j := 0; j < len(b); j++ {
357 if c == b[j] {
358 found = true
359 break
360 }
361 }
362 if !found {
363 break
364 }
365 }
366 return source[:i+1]
367 }
368
369
370 func TrimLeftLength(source, s []byte) int {
371 return len(source) - len(TrimLeft(source, s))
372 }
373
374
375 func TrimRightLength(source, s []byte) int {
376 return len(source) - len(TrimRight(source, s))
377 }
378
379
380 func TrimLeftSpaceLength(source []byte) int {
381 i := 0
382 for ; i < len(source); i++ {
383 if !IsSpace(source[i]) {
384 break
385 }
386 }
387 return i
388 }
389
390
391 func TrimRightSpaceLength(source []byte) int {
392 l := len(source)
393 i := l - 1
394 for ; i >= 0; i-- {
395 if !IsSpace(source[i]) {
396 break
397 }
398 }
399 if i < 0 {
400 return l
401 }
402 return l - 1 - i
403 }
404
405
406
407 func TrimLeftSpace(source []byte) []byte {
408 return TrimLeft(source, spaces)
409 }
410
411
412
413 func TrimRightSpace(source []byte) []byte {
414 return TrimRight(source, spaces)
415 }
416
417
418 func DoFullUnicodeCaseFolding(v []byte) []byte {
419 var rbuf []byte
420 cob := NewCopyOnWriteBuffer(v)
421 n := 0
422 for i := 0; i < len(v); i++ {
423 c := v[i]
424 if c < 0xb5 {
425 if c >= 0x41 && c <= 0x5a {
426
427 cob.Write(v[n:i])
428 _ = cob.WriteByte(c + 32)
429 n = i + 1
430 }
431 continue
432 }
433
434 if !utf8.RuneStart(c) {
435 continue
436 }
437 r, length := utf8.DecodeRune(v[i:])
438 if r == utf8.RuneError {
439 continue
440 }
441 folded, ok := unicodeCaseFoldings[r]
442 if !ok {
443 continue
444 }
445
446 cob.Write(v[n:i])
447 if rbuf == nil {
448 rbuf = make([]byte, 4)
449 }
450 for _, f := range folded {
451 l := utf8.EncodeRune(rbuf, f)
452 cob.Write(rbuf[:l])
453 }
454 i += length - 1
455 n = i + 1
456 }
457 if cob.IsCopied() {
458 cob.Write(v[n:])
459 }
460 return cob.Bytes()
461 }
462
463
464 func ReplaceSpaces(source []byte, repl byte) []byte {
465 var ret []byte
466 start := -1
467 for i, c := range source {
468 iss := IsSpace(c)
469 if start < 0 && iss {
470 start = i
471 continue
472 } else if start >= 0 && iss {
473 continue
474 } else if start >= 0 {
475 if ret == nil {
476 ret = make([]byte, 0, len(source))
477 ret = append(ret, source[:start]...)
478 }
479 ret = append(ret, repl)
480 start = -1
481 }
482 if ret != nil {
483 ret = append(ret, c)
484 }
485 }
486 if start >= 0 && ret != nil {
487 ret = append(ret, repl)
488 }
489 if ret == nil {
490 return source
491 }
492 return ret
493 }
494
495
496 func ToRune(source []byte, pos int) rune {
497 i := pos
498 for ; i >= 0; i-- {
499 if utf8.RuneStart(source[i]) {
500 break
501 }
502 }
503 r, _ := utf8.DecodeRune(source[i:])
504 return r
505 }
506
507
508 func ToValidRune(v rune) rune {
509 if v == 0 || !utf8.ValidRune(v) {
510 return rune(0xFFFD)
511 }
512 return v
513 }
514
515
516
517
518 func ToLinkReference(v []byte) string {
519 v = TrimLeftSpace(v)
520 v = TrimRightSpace(v)
521 v = DoFullUnicodeCaseFolding(v)
522 return string(ReplaceSpaces(v, ' '))
523 }
524
525 var htmlEscapeTable = [256][]byte{nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, []byte("""), nil, nil, nil, []byte("&"), nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, []byte("<"), nil, []byte(">"), nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil}
526
527
528
529 func EscapeHTMLByte(b byte) []byte {
530 return htmlEscapeTable[b]
531 }
532
533
534 func EscapeHTML(v []byte) []byte {
535 cob := NewCopyOnWriteBuffer(v)
536 n := 0
537 for i := 0; i < len(v); i++ {
538 c := v[i]
539 escaped := htmlEscapeTable[c]
540 if escaped != nil {
541 cob.Write(v[n:i])
542 cob.Write(escaped)
543 n = i + 1
544 }
545 }
546 if cob.IsCopied() {
547 cob.Write(v[n:])
548 }
549 return cob.Bytes()
550 }
551
552
553 func UnescapePunctuations(source []byte) []byte {
554 cob := NewCopyOnWriteBuffer(source)
555 limit := len(source)
556 n := 0
557 for i := 0; i < limit; {
558 c := source[i]
559 if i < limit-1 && c == '\\' && IsPunct(source[i+1]) {
560 cob.Write(source[n:i])
561 _ = cob.WriteByte(source[i+1])
562 i += 2
563 n = i
564 continue
565 }
566 i++
567 }
568 if cob.IsCopied() {
569 cob.Write(source[n:])
570 }
571 return cob.Bytes()
572 }
573
574
575 func ResolveNumericReferences(source []byte) []byte {
576 cob := NewCopyOnWriteBuffer(source)
577 buf := make([]byte, 6)
578 limit := len(source)
579 var ok bool
580 n := 0
581 for i := 0; i < limit; i++ {
582 if source[i] == '&' {
583 pos := i
584 next := i + 1
585 if next < limit && source[next] == '#' {
586 nnext := next + 1
587 if nnext < limit {
588 nc := source[nnext]
589
590 if nnext < limit && nc == 'x' || nc == 'X' {
591 start := nnext + 1
592 i, ok = ReadWhile(source, [2]int{start, limit}, IsHexDecimal)
593 if ok && i < limit && source[i] == ';' {
594 v, _ := strconv.ParseUint(BytesToReadOnlyString(source[start:i]), 16, 32)
595 cob.Write(source[n:pos])
596 n = i + 1
597 runeSize := utf8.EncodeRune(buf, ToValidRune(rune(v)))
598 cob.Write(buf[:runeSize])
599 continue
600 }
601
602 } else if nc >= '0' && nc <= '9' {
603 start := nnext
604 i, ok = ReadWhile(source, [2]int{start, limit}, IsNumeric)
605 if ok && i < limit && i-start < 8 && source[i] == ';' {
606 v, _ := strconv.ParseUint(BytesToReadOnlyString(source[start:i]), 0, 32)
607 cob.Write(source[n:pos])
608 n = i + 1
609 runeSize := utf8.EncodeRune(buf, ToValidRune(rune(v)))
610 cob.Write(buf[:runeSize])
611 continue
612 }
613 }
614 }
615 }
616 i = next - 1
617 }
618 }
619 if cob.IsCopied() {
620 cob.Write(source[n:])
621 }
622 return cob.Bytes()
623 }
624
625
626 func ResolveEntityNames(source []byte) []byte {
627 cob := NewCopyOnWriteBuffer(source)
628 limit := len(source)
629 var ok bool
630 n := 0
631 for i := 0; i < limit; i++ {
632 if source[i] == '&' {
633 pos := i
634 next := i + 1
635 if !(next < limit && source[next] == '#') {
636 start := next
637 i, ok = ReadWhile(source, [2]int{start, limit}, IsAlphaNumeric)
638 if ok && i < limit && source[i] == ';' {
639 name := BytesToReadOnlyString(source[start:i])
640 entity, ok := LookUpHTML5EntityByName(name)
641 if ok {
642 cob.Write(source[n:pos])
643 n = i + 1
644 cob.Write(entity.Characters)
645 continue
646 }
647 }
648 }
649 i = next - 1
650 }
651 }
652 if cob.IsCopied() {
653 cob.Write(source[n:])
654 }
655 return cob.Bytes()
656 }
657
658 var htmlSpace = []byte("%20")
659
660
661
662
663
664
665
666
667 func URLEscape(v []byte, resolveReference bool) []byte {
668 if resolveReference {
669 v = UnescapePunctuations(v)
670 v = ResolveNumericReferences(v)
671 v = ResolveEntityNames(v)
672 }
673 cob := NewCopyOnWriteBuffer(v)
674 limit := len(v)
675 n := 0
676
677 for i := 0; i < limit; {
678 c := v[i]
679 if urlEscapeTable[c] == 1 {
680 i++
681 continue
682 }
683 if c == '%' && i+2 < limit && IsHexDecimal(v[i+1]) && IsHexDecimal(v[i+1]) {
684 i += 3
685 continue
686 }
687 u8len := utf8lenTable[c]
688 if u8len == 99 {
689 i++
690 continue
691 }
692 if c == ' ' {
693 cob.Write(v[n:i])
694 cob.Write(htmlSpace)
695 i++
696 n = i
697 continue
698 }
699 if int(u8len) > len(v) {
700 u8len = int8(len(v) - 1)
701 }
702 if u8len == 0 {
703 i++
704 n = i
705 continue
706 }
707 cob.Write(v[n:i])
708 stop := i + int(u8len)
709 if stop > len(v) {
710 i++
711 n = i
712 continue
713 }
714 cob.Write(StringToReadOnlyBytes(url.QueryEscape(string(v[i:stop]))))
715 i += int(u8len)
716 n = i
717 }
718 if cob.IsCopied() && n < limit {
719 cob.Write(v[n:])
720 }
721 return cob.Bytes()
722 }
723
724
725
726 func FindURLIndex(b []byte) int {
727 i := 0
728 if !(len(b) > 0 && urlTable[b[i]]&7 == 7) {
729 return -1
730 }
731 i++
732 for ; i < len(b); i++ {
733 c := b[i]
734 if urlTable[c]&4 != 4 {
735 break
736 }
737 }
738 if i == 1 || i > 33 || i >= len(b) {
739 return -1
740 }
741 if b[i] != ':' {
742 return -1
743 }
744 i++
745 for ; i < len(b); i++ {
746 c := b[i]
747 if urlTable[c]&1 != 1 {
748 break
749 }
750 }
751 return i
752 }
753
754 var emailDomainRegexp = regexp.MustCompile(`^[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*`)
755
756
757 func FindEmailIndex(b []byte) int {
758
759 i := 0
760 for ; i < len(b); i++ {
761 c := b[i]
762 if emailTable[c]&1 != 1 {
763 break
764 }
765 }
766 if i == 0 {
767 return -1
768 }
769 if i >= len(b) || b[i] != '@' {
770 return -1
771 }
772 i++
773 if i >= len(b) {
774 return -1
775 }
776 match := emailDomainRegexp.FindSubmatchIndex(b[i:])
777 if match == nil {
778 return -1
779 }
780 return i + match[1]
781 }
782
783 var spaces = []byte(" \t\n\x0b\x0c\x0d")
784
785 var spaceTable = [256]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
786
787 var punctTable = [256]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
788
789
790
791 var urlEscapeTable = [256]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
792
793 var utf8lenTable = [256]int8{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 99, 99, 99, 99, 99, 99, 99, 99}
794
795 var urlTable = [256]uint8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 5, 5, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 1, 1, 0, 1, 0, 1, 1, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 1, 1, 1, 1, 1, 1, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
796
797 var emailTable = [256]uint8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
798
799
800 func UTF8Len(b byte) int8 {
801 return utf8lenTable[b]
802 }
803
804
805 func IsPunct(c byte) bool {
806 return punctTable[c] == 1
807 }
808
809
810 func IsPunctRune(r rune) bool {
811 return int32(r) <= 256 && IsPunct(byte(r)) || unicode.IsPunct(r)
812 }
813
814
815 func IsSpace(c byte) bool {
816 return spaceTable[c] == 1
817 }
818
819
820 func IsSpaceRune(r rune) bool {
821 return int32(r) <= 256 && IsSpace(byte(r)) || unicode.IsSpace(r)
822 }
823
824
825 func IsNumeric(c byte) bool {
826 return c >= '0' && c <= '9'
827 }
828
829
830 func IsHexDecimal(c byte) bool {
831 return c >= '0' && c <= '9' || c >= 'a' && c <= 'f' || c >= 'A' && c <= 'F'
832 }
833
834
835 func IsAlphaNumeric(c byte) bool {
836 return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9'
837 }
838
839
840 type BufWriter interface {
841 io.Writer
842 Available() int
843 Buffered() int
844 Flush() error
845 WriteByte(c byte) error
846 WriteRune(r rune) (size int, err error)
847 WriteString(s string) (int, error)
848 }
849
850
851 type PrioritizedValue struct {
852
853 Value interface{}
854
855 Priority int
856 }
857
858
859 type PrioritizedSlice []PrioritizedValue
860
861
862 func (s PrioritizedSlice) Sort() {
863 sort.Slice(s, func(i, j int) bool {
864 return s[i].Priority < s[j].Priority
865 })
866 }
867
868
869 func (s PrioritizedSlice) Remove(v interface{}) PrioritizedSlice {
870 i := 0
871 found := false
872 for ; i < len(s); i++ {
873 if s[i].Value == v {
874 found = true
875 break
876 }
877 }
878 if !found {
879 return s
880 }
881 return append(s[:i], s[i+1:]...)
882 }
883
884
885 func Prioritized(v interface{}, priority int) PrioritizedValue {
886 return PrioritizedValue{v, priority}
887 }
888
889 func bytesHash(b []byte) uint64 {
890 var hash uint64 = 5381
891 for _, c := range b {
892 hash = ((hash << 5) + hash) + uint64(c)
893 }
894 return hash
895 }
896
897
898
899 type BytesFilter interface {
900
901 Add([]byte)
902
903
904 Contains([]byte) bool
905
906
907 Extend(...[]byte) BytesFilter
908 }
909
910 type bytesFilter struct {
911 chars [256]uint8
912 threshold int
913 slots [][][]byte
914 }
915
916
917 func NewBytesFilter(elements ...[]byte) BytesFilter {
918 s := &bytesFilter{
919 threshold: 3,
920 slots: make([][][]byte, 64),
921 }
922 for _, element := range elements {
923 s.Add(element)
924 }
925 return s
926 }
927
928 func (s *bytesFilter) Add(b []byte) {
929 l := len(b)
930 m := s.threshold
931 if l < s.threshold {
932 m = l
933 }
934 for i := 0; i < m; i++ {
935 s.chars[b[i]] |= 1 << uint8(i)
936 }
937 h := bytesHash(b) % uint64(len(s.slots))
938 slot := s.slots[h]
939 if slot == nil {
940 slot = [][]byte{}
941 }
942 s.slots[h] = append(slot, b)
943 }
944
945 func (s *bytesFilter) Extend(bs ...[]byte) BytesFilter {
946 newFilter := NewBytesFilter().(*bytesFilter)
947 newFilter.chars = s.chars
948 newFilter.threshold = s.threshold
949 for k, v := range s.slots {
950 newSlot := make([][]byte, len(v))
951 copy(newSlot, v)
952 newFilter.slots[k] = v
953 }
954 for _, b := range bs {
955 newFilter.Add(b)
956 }
957 return newFilter
958 }
959
960 func (s *bytesFilter) Contains(b []byte) bool {
961 l := len(b)
962 m := s.threshold
963 if l < s.threshold {
964 m = l
965 }
966 for i := 0; i < m; i++ {
967 if (s.chars[b[i]] & (1 << uint8(i))) == 0 {
968 return false
969 }
970 }
971 h := bytesHash(b) % uint64(len(s.slots))
972 slot := s.slots[h]
973 if len(slot) == 0 {
974 return false
975 }
976 for _, element := range slot {
977 if bytes.Equal(element, b) {
978 return true
979 }
980 }
981 return false
982 }
983
View as plain text