package util
import (
"bytes"
"io"
"net/url"
"regexp"
"sort"
"strconv"
"unicode"
"unicode/utf8"
)
type CopyOnWriteBuffer struct {
buffer []byte
copied bool
}
func NewCopyOnWriteBuffer (buffer []byte ) CopyOnWriteBuffer {
return CopyOnWriteBuffer {
buffer : buffer ,
copied : false ,
}
}
func (b *CopyOnWriteBuffer ) Write (value []byte ) {
if !b .copied {
b .buffer = make ([]byte , 0 , len (b .buffer )+20 )
b .copied = true
}
b .buffer = append (b .buffer , value ...)
}
func (b *CopyOnWriteBuffer ) WriteString (value string ) {
b .Write (StringToReadOnlyBytes (value ))
}
func (b *CopyOnWriteBuffer ) Append (value []byte ) {
if !b .copied {
tmp := make ([]byte , len (b .buffer ), len (b .buffer )+20 )
copy (tmp , b .buffer )
b .buffer = tmp
b .copied = true
}
b .buffer = append (b .buffer , value ...)
}
func (b *CopyOnWriteBuffer ) AppendString (value string ) {
b .Append (StringToReadOnlyBytes (value ))
}
func (b *CopyOnWriteBuffer ) WriteByte (c byte ) error {
if !b .copied {
b .buffer = make ([]byte , 0 , len (b .buffer )+20 )
b .copied = true
}
b .buffer = append (b .buffer , c )
return nil
}
func (b *CopyOnWriteBuffer ) AppendByte (c byte ) {
if !b .copied {
tmp := make ([]byte , len (b .buffer ), len (b .buffer )+20 )
copy (tmp , b .buffer )
b .buffer = tmp
b .copied = true
}
b .buffer = append (b .buffer , c )
}
func (b *CopyOnWriteBuffer ) Bytes () []byte {
return b .buffer
}
func (b *CopyOnWriteBuffer ) IsCopied () bool {
return b .copied
}
func IsEscapedPunctuation (source []byte , i int ) bool {
return source [i ] == '\\' && i < len (source )-1 && IsPunct (source [i +1 ])
}
func ReadWhile (source []byte , index [2 ]int , pred func (byte ) bool ) (int , bool ) {
j := index [0 ]
ok := false
for ; j < index [1 ]; j ++ {
c1 := source [j ]
if pred (c1 ) {
ok = true
continue
}
break
}
return j , ok
}
func IsBlank (bs []byte ) bool {
for _ , b := range bs {
if !IsSpace (b ) {
return false
}
}
return true
}
func VisualizeSpaces (bs []byte ) []byte {
bs = bytes .Replace (bs , []byte (" " ), []byte ("[SPACE]" ), -1 )
bs = bytes .Replace (bs , []byte ("\t" ), []byte ("[TAB]" ), -1 )
bs = bytes .Replace (bs , []byte ("\n" ), []byte ("[NEWLINE]\n" ), -1 )
bs = bytes .Replace (bs , []byte ("\r" ), []byte ("[CR]" ), -1 )
bs = bytes .Replace (bs , []byte ("\v" ), []byte ("[VTAB]" ), -1 )
bs = bytes .Replace (bs , []byte ("\x00" ), []byte ("[NUL]" ), -1 )
bs = bytes .Replace (bs , []byte ("\ufffd" ), []byte ("[U+FFFD]" ), -1 )
return bs
}
func TabWidth (currentPos int ) int {
return 4 - currentPos %4
}
func IndentPosition (bs []byte , currentPos , width int ) (pos , padding int ) {
return IndentPositionPadding (bs , currentPos , 0 , width )
}
func IndentPositionPadding (bs []byte , currentPos , paddingv , width int ) (pos , padding int ) {
if width == 0 {
return 0 , paddingv
}
w := 0
i := 0
l := len (bs )
for ; i < l ; i ++ {
if bs [i ] == '\t' && w < width {
w += TabWidth (currentPos + w )
} else if bs [i ] == ' ' && w < width {
w ++
} else {
break
}
}
if w >= width {
return i - paddingv , w - width
}
return -1 , -1
}
func DedentPosition (bs []byte , currentPos , width int ) (pos , padding int ) {
if width == 0 {
return 0 , 0
}
w := 0
l := len (bs )
i := 0
for ; i < l ; i ++ {
if bs [i ] == '\t' {
w += TabWidth (currentPos + w )
} else if bs [i ] == ' ' {
w ++
} else {
break
}
}
if w >= width {
return i , w - width
}
return i , 0
}
func DedentPositionPadding (bs []byte , currentPos , paddingv , width int ) (pos , padding int ) {
if width == 0 {
return 0 , paddingv
}
w := 0
i := 0
l := len (bs )
for ; i < l ; i ++ {
if bs [i ] == '\t' {
w += TabWidth (currentPos + w )
} else if bs [i ] == ' ' {
w ++
} else {
break
}
}
if w >= width {
return i - paddingv , w - width
}
return i - paddingv , 0
}
func IndentWidth (bs []byte , currentPos int ) (width , pos int ) {
l := len (bs )
for i := 0 ; i < l ; i ++ {
b := bs [i ]
if b == ' ' {
width ++
pos ++
} else if b == '\t' {
width += TabWidth (currentPos + width )
pos ++
} else {
break
}
}
return
}
func FirstNonSpacePosition (bs []byte ) int {
i := 0
for ; i < len (bs ); i ++ {
c := bs [i ]
if c == ' ' || c == '\t' {
continue
}
if c == '\n' {
return -1
}
return i
}
return -1
}
func FindClosure (bs []byte , opener , closure byte , codeSpan , allowNesting bool ) int {
i := 0
opened := 1
codeSpanOpener := 0
for i < len (bs ) {
c := bs [i ]
if codeSpan && codeSpanOpener != 0 && c == '`' {
codeSpanCloser := 0
for ; i < len (bs ); i ++ {
if bs [i ] == '`' {
codeSpanCloser ++
} else {
i --
break
}
}
if codeSpanCloser == codeSpanOpener {
codeSpanOpener = 0
}
} else if codeSpanOpener == 0 && c == '\\' && i < len (bs )-1 && IsPunct (bs [i +1 ]) {
i += 2
continue
} else if codeSpan && codeSpanOpener == 0 && c == '`' {
for ; i < len (bs ); i ++ {
if bs [i ] == '`' {
codeSpanOpener ++
} else {
i --
break
}
}
} else if (codeSpan && codeSpanOpener == 0 ) || !codeSpan {
if c == closure {
opened --
if opened == 0 {
return i
}
} else if c == opener {
if !allowNesting {
return -1
}
opened ++
}
}
i ++
}
return -1
}
func TrimLeft (source , b []byte ) []byte {
i := 0
for ; i < len (source ); i ++ {
c := source [i ]
found := false
for j := 0 ; j < len (b ); j ++ {
if c == b [j ] {
found = true
break
}
}
if !found {
break
}
}
return source [i :]
}
func TrimRight (source , b []byte ) []byte {
i := len (source ) - 1
for ; i >= 0 ; i -- {
c := source [i ]
found := false
for j := 0 ; j < len (b ); j ++ {
if c == b [j ] {
found = true
break
}
}
if !found {
break
}
}
return source [:i +1 ]
}
func TrimLeftLength (source , s []byte ) int {
return len (source ) - len (TrimLeft (source , s ))
}
func TrimRightLength (source , s []byte ) int {
return len (source ) - len (TrimRight (source , s ))
}
func TrimLeftSpaceLength (source []byte ) int {
i := 0
for ; i < len (source ); i ++ {
if !IsSpace (source [i ]) {
break
}
}
return i
}
func TrimRightSpaceLength (source []byte ) int {
l := len (source )
i := l - 1
for ; i >= 0 ; i -- {
if !IsSpace (source [i ]) {
break
}
}
if i < 0 {
return l
}
return l - 1 - i
}
func TrimLeftSpace (source []byte ) []byte {
return TrimLeft (source , spaces )
}
func TrimRightSpace (source []byte ) []byte {
return TrimRight (source , spaces )
}
func DoFullUnicodeCaseFolding (v []byte ) []byte {
var rbuf []byte
cob := NewCopyOnWriteBuffer (v )
n := 0
for i := 0 ; i < len (v ); i ++ {
c := v [i ]
if c < 0xb5 {
if c >= 0x41 && c <= 0x5a {
cob .Write (v [n :i ])
_ = cob .WriteByte (c + 32 )
n = i + 1
}
continue
}
if !utf8 .RuneStart (c ) {
continue
}
r , length := utf8 .DecodeRune (v [i :])
if r == utf8 .RuneError {
continue
}
folded , ok := unicodeCaseFoldings [r ]
if !ok {
continue
}
cob .Write (v [n :i ])
if rbuf == nil {
rbuf = make ([]byte , 4 )
}
for _ , f := range folded {
l := utf8 .EncodeRune (rbuf , f )
cob .Write (rbuf [:l ])
}
i += length - 1
n = i + 1
}
if cob .IsCopied () {
cob .Write (v [n :])
}
return cob .Bytes ()
}
func ReplaceSpaces (source []byte , repl byte ) []byte {
var ret []byte
start := -1
for i , c := range source {
iss := IsSpace (c )
if start < 0 && iss {
start = i
continue
} else if start >= 0 && iss {
continue
} else if start >= 0 {
if ret == nil {
ret = make ([]byte , 0 , len (source ))
ret = append (ret , source [:start ]...)
}
ret = append (ret , repl )
start = -1
}
if ret != nil {
ret = append (ret , c )
}
}
if start >= 0 && ret != nil {
ret = append (ret , repl )
}
if ret == nil {
return source
}
return ret
}
func ToRune (source []byte , pos int ) rune {
i := pos
for ; i >= 0 ; i -- {
if utf8 .RuneStart (source [i ]) {
break
}
}
r , _ := utf8 .DecodeRune (source [i :])
return r
}
func ToValidRune (v rune ) rune {
if v == 0 || !utf8 .ValidRune (v ) {
return rune (0xFFFD )
}
return v
}
func ToLinkReference (v []byte ) string {
v = TrimLeftSpace (v )
v = TrimRightSpace (v )
v = DoFullUnicodeCaseFolding (v )
return string (ReplaceSpaces (v , ' ' ))
}
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 }
func EscapeHTMLByte (b byte ) []byte {
return htmlEscapeTable [b ]
}
func EscapeHTML (v []byte ) []byte {
cob := NewCopyOnWriteBuffer (v )
n := 0
for i := 0 ; i < len (v ); i ++ {
c := v [i ]
escaped := htmlEscapeTable [c ]
if escaped != nil {
cob .Write (v [n :i ])
cob .Write (escaped )
n = i + 1
}
}
if cob .IsCopied () {
cob .Write (v [n :])
}
return cob .Bytes ()
}
func UnescapePunctuations (source []byte ) []byte {
cob := NewCopyOnWriteBuffer (source )
limit := len (source )
n := 0
for i := 0 ; i < limit ; {
c := source [i ]
if i < limit -1 && c == '\\' && IsPunct (source [i +1 ]) {
cob .Write (source [n :i ])
_ = cob .WriteByte (source [i +1 ])
i += 2
n = i
continue
}
i ++
}
if cob .IsCopied () {
cob .Write (source [n :])
}
return cob .Bytes ()
}
func ResolveNumericReferences (source []byte ) []byte {
cob := NewCopyOnWriteBuffer (source )
buf := make ([]byte , 6 )
limit := len (source )
var ok bool
n := 0
for i := 0 ; i < limit ; i ++ {
if source [i ] == '&' {
pos := i
next := i + 1
if next < limit && source [next ] == '#' {
nnext := next + 1
if nnext < limit {
nc := source [nnext ]
if nnext < limit && nc == 'x' || nc == 'X' {
start := nnext + 1
i , ok = ReadWhile (source , [2 ]int {start , limit }, IsHexDecimal )
if ok && i < limit && source [i ] == ';' {
v , _ := strconv .ParseUint (BytesToReadOnlyString (source [start :i ]), 16 , 32 )
cob .Write (source [n :pos ])
n = i + 1
runeSize := utf8 .EncodeRune (buf , ToValidRune (rune (v )))
cob .Write (buf [:runeSize ])
continue
}
} else if nc >= '0' && nc <= '9' {
start := nnext
i , ok = ReadWhile (source , [2 ]int {start , limit }, IsNumeric )
if ok && i < limit && i -start < 8 && source [i ] == ';' {
v , _ := strconv .ParseUint (BytesToReadOnlyString (source [start :i ]), 0 , 32 )
cob .Write (source [n :pos ])
n = i + 1
runeSize := utf8 .EncodeRune (buf , ToValidRune (rune (v )))
cob .Write (buf [:runeSize ])
continue
}
}
}
}
i = next - 1
}
}
if cob .IsCopied () {
cob .Write (source [n :])
}
return cob .Bytes ()
}
func ResolveEntityNames (source []byte ) []byte {
cob := NewCopyOnWriteBuffer (source )
limit := len (source )
var ok bool
n := 0
for i := 0 ; i < limit ; i ++ {
if source [i ] == '&' {
pos := i
next := i + 1
if !(next < limit && source [next ] == '#' ) {
start := next
i , ok = ReadWhile (source , [2 ]int {start , limit }, IsAlphaNumeric )
if ok && i < limit && source [i ] == ';' {
name := BytesToReadOnlyString (source [start :i ])
entity , ok := LookUpHTML5EntityByName (name )
if ok {
cob .Write (source [n :pos ])
n = i + 1
cob .Write (entity .Characters )
continue
}
}
}
i = next - 1
}
}
if cob .IsCopied () {
cob .Write (source [n :])
}
return cob .Bytes ()
}
var htmlSpace = []byte ("%20" )
func URLEscape (v []byte , resolveReference bool ) []byte {
if resolveReference {
v = UnescapePunctuations (v )
v = ResolveNumericReferences (v )
v = ResolveEntityNames (v )
}
cob := NewCopyOnWriteBuffer (v )
limit := len (v )
n := 0
for i := 0 ; i < limit ; {
c := v [i ]
if urlEscapeTable [c ] == 1 {
i ++
continue
}
if c == '%' && i +2 < limit && IsHexDecimal (v [i +1 ]) && IsHexDecimal (v [i +1 ]) {
i += 3
continue
}
u8len := utf8lenTable [c ]
if u8len == 99 {
i ++
continue
}
if c == ' ' {
cob .Write (v [n :i ])
cob .Write (htmlSpace )
i ++
n = i
continue
}
if int (u8len ) > len (v ) {
u8len = int8 (len (v ) - 1 )
}
if u8len == 0 {
i ++
n = i
continue
}
cob .Write (v [n :i ])
stop := i + int (u8len )
if stop > len (v ) {
i ++
n = i
continue
}
cob .Write (StringToReadOnlyBytes (url .QueryEscape (string (v [i :stop ]))))
i += int (u8len )
n = i
}
if cob .IsCopied () && n < limit {
cob .Write (v [n :])
}
return cob .Bytes ()
}
func FindURLIndex (b []byte ) int {
i := 0
if !(len (b ) > 0 && urlTable [b [i ]]&7 == 7 ) {
return -1
}
i ++
for ; i < len (b ); i ++ {
c := b [i ]
if urlTable [c ]&4 != 4 {
break
}
}
if i == 1 || i > 33 || i >= len (b ) {
return -1
}
if b [i ] != ':' {
return -1
}
i ++
for ; i < len (b ); i ++ {
c := b [i ]
if urlTable [c ]&1 != 1 {
break
}
}
return i
}
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])?)*` )
func FindEmailIndex (b []byte ) int {
i := 0
for ; i < len (b ); i ++ {
c := b [i ]
if emailTable [c ]&1 != 1 {
break
}
}
if i == 0 {
return -1
}
if i >= len (b ) || b [i ] != '@' {
return -1
}
i ++
if i >= len (b ) {
return -1
}
match := emailDomainRegexp .FindSubmatchIndex (b [i :])
if match == nil {
return -1
}
return i + match [1 ]
}
var spaces = []byte (" \t\n\x0b\x0c\x0d" )
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 }
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 }
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 }
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 }
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 }
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 }
func UTF8Len (b byte ) int8 {
return utf8lenTable [b ]
}
func IsPunct (c byte ) bool {
return punctTable [c ] == 1
}
func IsPunctRune (r rune ) bool {
return unicode .IsSymbol (r ) || unicode .IsPunct (r )
}
func IsSpace (c byte ) bool {
return spaceTable [c ] == 1
}
func IsSpaceRune (r rune ) bool {
return int32 (r ) <= 256 && IsSpace (byte (r )) || unicode .IsSpace (r )
}
func IsNumeric (c byte ) bool {
return c >= '0' && c <= '9'
}
func IsHexDecimal (c byte ) bool {
return c >= '0' && c <= '9' || c >= 'a' && c <= 'f' || c >= 'A' && c <= 'F'
}
func IsAlphaNumeric (c byte ) bool {
return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9'
}
type BufWriter interface {
io .Writer
Available () int
Buffered () int
Flush () error
WriteByte (c byte ) error
WriteRune (r rune ) (size int , err error )
WriteString (s string ) (int , error )
}
type PrioritizedValue struct {
Value interface {}
Priority int
}
type PrioritizedSlice []PrioritizedValue
func (s PrioritizedSlice ) Sort () {
sort .Slice (s , func (i , j int ) bool {
return s [i ].Priority < s [j ].Priority
})
}
func (s PrioritizedSlice ) Remove (v interface {}) PrioritizedSlice {
i := 0
found := false
for ; i < len (s ); i ++ {
if s [i ].Value == v {
found = true
break
}
}
if !found {
return s
}
return append (s [:i ], s [i +1 :]...)
}
func Prioritized (v interface {}, priority int ) PrioritizedValue {
return PrioritizedValue {v , priority }
}
func bytesHash(b []byte ) uint64 {
var hash uint64 = 5381
for _ , c := range b {
hash = ((hash << 5 ) + hash ) + uint64 (c )
}
return hash
}
type BytesFilter interface {
Add ([]byte )
Contains ([]byte ) bool
Extend (...[]byte ) BytesFilter
}
type bytesFilter struct {
chars [256 ]uint8
threshold int
slots [][][]byte
}
func NewBytesFilter (elements ...[]byte ) BytesFilter {
s := &bytesFilter {
threshold : 3 ,
slots : make ([][][]byte , 64 ),
}
for _ , element := range elements {
s .Add (element )
}
return s
}
func (s *bytesFilter ) Add (b []byte ) {
l := len (b )
m := s .threshold
if l < s .threshold {
m = l
}
for i := 0 ; i < m ; i ++ {
s .chars [b [i ]] |= 1 << uint8 (i )
}
h := bytesHash (b ) % uint64 (len (s .slots ))
slot := s .slots [h ]
if slot == nil {
slot = [][]byte {}
}
s .slots [h ] = append (slot , b )
}
func (s *bytesFilter ) Extend (bs ...[]byte ) BytesFilter {
newFilter := NewBytesFilter ().(*bytesFilter )
newFilter .chars = s .chars
newFilter .threshold = s .threshold
for k , v := range s .slots {
newSlot := make ([][]byte , len (v ))
copy (newSlot , v )
newFilter .slots [k ] = v
}
for _ , b := range bs {
newFilter .Add (b )
}
return newFilter
}
func (s *bytesFilter ) Contains (b []byte ) bool {
l := len (b )
m := s .threshold
if l < s .threshold {
m = l
}
for i := 0 ; i < m ; i ++ {
if (s .chars [b [i ]] & (1 << uint8 (i ))) == 0 {
return false
}
}
h := bytesHash (b ) % uint64 (len (s .slots ))
slot := s .slots [h ]
if len (slot ) == 0 {
return false
}
for _ , element := range slot {
if bytes .Equal (element , b ) {
return true
}
}
return false
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .