// Package util provides utility functions for the goldmark.
package util import ( ) // A CopyOnWriteBuffer is a byte buffer that copies buffer when // it need to be changed. type CopyOnWriteBuffer struct { buffer []byte copied bool } // NewCopyOnWriteBuffer returns a new CopyOnWriteBuffer. func ( []byte) CopyOnWriteBuffer { return CopyOnWriteBuffer{ buffer: , copied: false, } } // Write writes given bytes to the buffer. // Write allocate new buffer and clears it at the first time. func ( *CopyOnWriteBuffer) ( []byte) { if !.copied { .buffer = make([]byte, 0, len(.buffer)+20) .copied = true } .buffer = append(.buffer, ...) } // WriteString writes given string to the buffer. // WriteString allocate new buffer and clears it at the first time. func ( *CopyOnWriteBuffer) ( string) { .Write(StringToReadOnlyBytes()) } // Append appends given bytes to the buffer. // Append copy buffer at the first time. func ( *CopyOnWriteBuffer) ( []byte) { if !.copied { := make([]byte, len(.buffer), len(.buffer)+20) copy(, .buffer) .buffer = .copied = true } .buffer = append(.buffer, ...) } // AppendString appends given string to the buffer. // AppendString copy buffer at the first time. func ( *CopyOnWriteBuffer) ( string) { .Append(StringToReadOnlyBytes()) } // WriteByte writes the given byte to the buffer. // WriteByte allocate new buffer and clears it at the first time. func ( *CopyOnWriteBuffer) ( byte) error { if !.copied { .buffer = make([]byte, 0, len(.buffer)+20) .copied = true } .buffer = append(.buffer, ) return nil } // AppendByte appends given bytes to the buffer. // AppendByte copy buffer at the first time. func ( *CopyOnWriteBuffer) ( byte) { if !.copied { := make([]byte, len(.buffer), len(.buffer)+20) copy(, .buffer) .buffer = .copied = true } .buffer = append(.buffer, ) } // Bytes returns bytes of this buffer. func ( *CopyOnWriteBuffer) () []byte { return .buffer } // IsCopied returns true if buffer has been copied, otherwise false. func ( *CopyOnWriteBuffer) () bool { return .copied } // IsEscapedPunctuation returns true if character at a given index i // is an escaped punctuation, otherwise false. func ( []byte, int) bool { return [] == '\\' && < len()-1 && IsPunct([+1]) } // ReadWhile read the given source while pred is true. func ( []byte, [2]int, func(byte) bool) (int, bool) { := [0] := false for ; < [1]; ++ { := [] if () { = true continue } break } return , } // IsBlank returns true if the given string is all space characters. func ( []byte) bool { for , := range { if !IsSpace() { return false } } return true } // VisualizeSpaces visualize invisible space characters. func ( []byte) []byte { = bytes.Replace(, []byte(" "), []byte("[SPACE]"), -1) = bytes.Replace(, []byte("\t"), []byte("[TAB]"), -1) = bytes.Replace(, []byte("\n"), []byte("[NEWLINE]\n"), -1) = bytes.Replace(, []byte("\r"), []byte("[CR]"), -1) = bytes.Replace(, []byte("\v"), []byte("[VTAB]"), -1) = bytes.Replace(, []byte("\x00"), []byte("[NUL]"), -1) = bytes.Replace(, []byte("\ufffd"), []byte("[U+FFFD]"), -1) return } // TabWidth calculates actual width of a tab at the given position. func ( int) int { return 4 - %4 } // IndentPosition searches an indent position with the given width for the given line. // If the line contains tab characters, paddings may be not zero. // currentPos==0 and width==2: // // position: 0 1 // [TAB]aaaa // width: 1234 5678 // // width=2 is in the tab character. In this case, IndentPosition returns // (pos=1, padding=2). func ( []byte, , int) (, int) { return IndentPositionPadding(, , 0, ) } // IndentPositionPadding searches an indent position with the given width for the given line. // This function is mostly same as IndentPosition except this function // takes account into additional paddings. func ( []byte, , , int) (, int) { if == 0 { return 0, } := 0 := 0 := len() for ; < ; ++ { if [] == '\t' && < { += TabWidth( + ) } else if [] == ' ' && < { ++ } else { break } } if >= { return - , - } return -1, -1 } // DedentPosition dedents lines by the given width. // // Deprecated: This function has bugs. Use util.IndentPositionPadding and util.FirstNonSpacePosition. func ( []byte, , int) (, int) { if == 0 { return 0, 0 } := 0 := len() := 0 for ; < ; ++ { if [] == '\t' { += TabWidth( + ) } else if [] == ' ' { ++ } else { break } } if >= { return , - } return , 0 } // DedentPositionPadding dedents lines by the given width. // This function is mostly same as DedentPosition except this function // takes account into additional paddings. // // Deprecated: This function has bugs. Use util.IndentPositionPadding and util.FirstNonSpacePosition. func ( []byte, , , int) (, int) { if == 0 { return 0, } := 0 := 0 := len() for ; < ; ++ { if [] == '\t' { += TabWidth( + ) } else if [] == ' ' { ++ } else { break } } if >= { return - , - } return - , 0 } // IndentWidth calculate an indent width for the given line. func ( []byte, int) (, int) { := len() for := 0; < ; ++ { := [] if == ' ' { ++ ++ } else if == '\t' { += TabWidth( + ) ++ } else { break } } return } // FirstNonSpacePosition returns a position line that is a first nonspace // character. func ( []byte) int { := 0 for ; < len(); ++ { := [] if == ' ' || == '\t' { continue } if == '\n' { return -1 } return } return -1 } // FindClosure returns a position that closes the given opener. // If codeSpan is set true, it ignores characters in code spans. // If allowNesting is set true, closures correspond to nested opener will be // ignored. // // Deprecated: This function can not handle newlines. Many elements // can be existed over multiple lines(e.g. link labels). // Use text.Reader.FindClosure. func ( []byte, , byte, , bool) int { := 0 := 1 := 0 for < len() { := [] if && != 0 && == '`' { := 0 for ; < len(); ++ { if [] == '`' { ++ } else { -- break } } if == { = 0 } } else if == 0 && == '\\' && < len()-1 && IsPunct([+1]) { += 2 continue } else if && == 0 && == '`' { for ; < len(); ++ { if [] == '`' { ++ } else { -- break } } } else if ( && == 0) || ! { if == { -- if == 0 { return } } else if == { if ! { return -1 } ++ } } ++ } return -1 } // TrimLeft trims characters in the given s from head of the source. // bytes.TrimLeft offers same functionalities, but bytes.TrimLeft // allocates new buffer for the result. func (, []byte) []byte { := 0 for ; < len(); ++ { := [] := false for := 0; < len(); ++ { if == [] { = true break } } if ! { break } } return [:] } // TrimRight trims characters in the given s from tail of the source. func (, []byte) []byte { := len() - 1 for ; >= 0; -- { := [] := false for := 0; < len(); ++ { if == [] { = true break } } if ! { break } } return [:+1] } // TrimLeftLength returns a length of leading specified characters. func (, []byte) int { return len() - len(TrimLeft(, )) } // TrimRightLength returns a length of trailing specified characters. func (, []byte) int { return len() - len(TrimRight(, )) } // TrimLeftSpaceLength returns a length of leading space characters. func ( []byte) int { := 0 for ; < len(); ++ { if !IsSpace([]) { break } } return } // TrimRightSpaceLength returns a length of trailing space characters. func ( []byte) int { := len() := - 1 for ; >= 0; -- { if !IsSpace([]) { break } } if < 0 { return } return - 1 - } // TrimLeftSpace returns a subslice of the given string by slicing off all leading // space characters. func ( []byte) []byte { return TrimLeft(, spaces) } // TrimRightSpace returns a subslice of the given string by slicing off all trailing // space characters. func ( []byte) []byte { return TrimRight(, spaces) } // DoFullUnicodeCaseFolding performs full unicode case folding to given bytes. func ( []byte) []byte { var []byte := NewCopyOnWriteBuffer() := 0 for := 0; < len(); ++ { := [] if < 0xb5 { if >= 0x41 && <= 0x5a { // A-Z to a-z .Write([:]) _ = .WriteByte( + 32) = + 1 } continue } if !utf8.RuneStart() { continue } , := utf8.DecodeRune([:]) if == utf8.RuneError { continue } , := unicodeCaseFoldings[] if ! { continue } .Write([:]) if == nil { = make([]byte, 4) } for , := range { := utf8.EncodeRune(, ) .Write([:]) } += - 1 = + 1 } if .IsCopied() { .Write([:]) } return .Bytes() } // ReplaceSpaces replaces sequence of spaces with the given repl. func ( []byte, byte) []byte { var []byte := -1 for , := range { := IsSpace() if < 0 && { = continue } else if >= 0 && { continue } else if >= 0 { if == nil { = make([]byte, 0, len()) = append(, [:]...) } = append(, ) = -1 } if != nil { = append(, ) } } if >= 0 && != nil { = append(, ) } if == nil { return } return } // ToRune decode given bytes start at pos and returns a rune. func ( []byte, int) rune { := for ; >= 0; -- { if utf8.RuneStart([]) { break } } , := utf8.DecodeRune([:]) return } // ToValidRune returns 0xFFFD if the given rune is invalid, otherwise v. func ( rune) rune { if == 0 || !utf8.ValidRune() { return rune(0xFFFD) } return } // ToLinkReference converts given bytes into a valid link reference string. // ToLinkReference performs unicode case folding, trims leading and trailing spaces, converts into lower // case and replace spaces with a single space character. func ( []byte) string { = TrimLeftSpace() = TrimRightSpace() = DoFullUnicodeCaseFolding() return string(ReplaceSpaces(, ' ')) } 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("&quot;"), nil, nil, nil, []byte("&amp;"), nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, []byte("&lt;"), nil, []byte("&gt;"), nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil} //nolint:golint,lll // EscapeHTMLByte returns HTML escaped bytes if the given byte should be escaped, // otherwise nil. func ( byte) []byte { return htmlEscapeTable[] } // EscapeHTML escapes characters that should be escaped in HTML text. func ( []byte) []byte { := NewCopyOnWriteBuffer() := 0 for := 0; < len(); ++ { := [] := htmlEscapeTable[] if != nil { .Write([:]) .Write() = + 1 } } if .IsCopied() { .Write([:]) } return .Bytes() } // UnescapePunctuations unescapes blackslash escaped punctuations. func ( []byte) []byte { := NewCopyOnWriteBuffer() := len() := 0 for := 0; < ; { := [] if < -1 && == '\\' && IsPunct([+1]) { .Write([:]) _ = .WriteByte([+1]) += 2 = continue } ++ } if .IsCopied() { .Write([:]) } return .Bytes() } // ResolveNumericReferences resolve numeric references like '&#1234;" . func ( []byte) []byte { := NewCopyOnWriteBuffer() := make([]byte, 6) := len() var bool := 0 for := 0; < ; ++ { if [] == '&' { := := + 1 if < && [] == '#' { := + 1 if < { := [] // code point like #x22; if < && == 'x' || == 'X' { := + 1 , = ReadWhile(, [2]int{, }, IsHexDecimal) if && < && [] == ';' { , := strconv.ParseUint(BytesToReadOnlyString([:]), 16, 32) .Write([:]) = + 1 := utf8.EncodeRune(, ToValidRune(rune())) .Write([:]) continue } // code point like #1234; } else if >= '0' && <= '9' { := , = ReadWhile(, [2]int{, }, IsNumeric) if && < && - < 8 && [] == ';' { , := strconv.ParseUint(BytesToReadOnlyString([:]), 0, 32) .Write([:]) = + 1 := utf8.EncodeRune(, ToValidRune(rune())) .Write([:]) continue } } } } = - 1 } } if .IsCopied() { .Write([:]) } return .Bytes() } // ResolveEntityNames resolve entity references like '&ouml;" . func ( []byte) []byte { := NewCopyOnWriteBuffer() := len() var bool := 0 for := 0; < ; ++ { if [] == '&' { := := + 1 if !( < && [] == '#') { := , = ReadWhile(, [2]int{, }, IsAlphaNumeric) if && < && [] == ';' { := BytesToReadOnlyString([:]) , := LookUpHTML5EntityByName() if { .Write([:]) = + 1 .Write(.Characters) continue } } } = - 1 } } if .IsCopied() { .Write([:]) } return .Bytes() } var htmlSpace = []byte("%20") // URLEscape escape the given URL. // If resolveReference is set true: // 1. unescape punctuations // 2. resolve numeric references // 3. resolve entity references // // URL encoded values (%xx) are kept as is. func ( []byte, bool) []byte { if { = UnescapePunctuations() = ResolveNumericReferences() = ResolveEntityNames() } := NewCopyOnWriteBuffer() := len() := 0 for := 0; < ; { := [] if urlEscapeTable[] == 1 { ++ continue } if == '%' && +2 < && IsHexDecimal([+1]) && IsHexDecimal([+1]) { += 3 continue } := utf8lenTable[] if == 99 { // invalid utf8 leading byte, skip it ++ continue } if == ' ' { .Write([:]) .Write(htmlSpace) ++ = continue } if int() > len() { = int8(len() - 1) } if == 0 { ++ = continue } .Write([:]) := + int() if > len() { ++ = continue } .Write(StringToReadOnlyBytes(url.QueryEscape(string([:])))) += int() = } if .IsCopied() && < { .Write([:]) } return .Bytes() } // FindURLIndex returns a stop index value if the given bytes seem an URL. // This function is equivalent to [A-Za-z][A-Za-z0-9.+-]{1,31}:[^<>\x00-\x20]* . func ( []byte) int { := 0 if !(len() > 0 && urlTable[[]]&7 == 7) { return -1 } ++ for ; < len(); ++ { := [] if urlTable[]&4 != 4 { break } } if == 1 || > 33 || >= len() { return -1 } if [] != ':' { return -1 } ++ for ; < len(); ++ { := [] if urlTable[]&1 != 1 { break } } return } 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])?)*`) //nolint:golint,lll // FindEmailIndex returns a stop index value if the given bytes seem an email address. func ( []byte) int { // TODO: eliminate regexps := 0 for ; < len(); ++ { := [] if emailTable[]&1 != 1 { break } } if == 0 { return -1 } if >= len() || [] != '@' { return -1 } ++ if >= len() { return -1 } := emailDomainRegexp.FindSubmatchIndex([:]) if == nil { return -1 } return + [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} //nolint:golint,lll 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} //nolint:golint,lll // a-zA-Z0-9, ;/?:@&=+$,-_.!~*'()# 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} //nolint:golint,lll 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} //nolint:golint,lll 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} //nolint:golint,lll 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} //nolint:golint,lll // UTF8Len returns a byte length of the utf-8 character. func ( byte) int8 { return utf8lenTable[] } // IsPunct returns true if the given character is a punctuation, otherwise false. func ( byte) bool { return punctTable[] == 1 } // IsPunctRune returns true if the given rune is a punctuation, otherwise false. func ( rune) bool { return unicode.IsSymbol() || unicode.IsPunct() } // IsSpace returns true if the given character is a space, otherwise false. func ( byte) bool { return spaceTable[] == 1 } // IsSpaceRune returns true if the given rune is a space, otherwise false. func ( rune) bool { return int32() <= 256 && IsSpace(byte()) || unicode.IsSpace() } // IsNumeric returns true if the given character is a numeric, otherwise false. func ( byte) bool { return >= '0' && <= '9' } // IsHexDecimal returns true if the given character is a hexdecimal, otherwise false. func ( byte) bool { return >= '0' && <= '9' || >= 'a' && <= 'f' || >= 'A' && <= 'F' } // IsAlphaNumeric returns true if the given character is a alphabet or a numeric, otherwise false. func ( byte) bool { return >= 'a' && <= 'z' || >= 'A' && <= 'Z' || >= '0' && <= '9' } // A BufWriter is a subset of the bufio.Writer . 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) } // A PrioritizedValue struct holds pair of an arbitrary value and a priority. type PrioritizedValue struct { // Value is an arbitrary value that you want to prioritize. Value interface{} // Priority is a priority of the value. Priority int } // PrioritizedSlice is a slice of the PrioritizedValues. type PrioritizedSlice []PrioritizedValue // Sort sorts the PrioritizedSlice in ascending order. func ( PrioritizedSlice) () { sort.Slice(, func(, int) bool { return [].Priority < [].Priority }) } // Remove removes the given value from this slice. func ( PrioritizedSlice) ( interface{}) PrioritizedSlice { := 0 := false for ; < len(); ++ { if [].Value == { = true break } } if ! { return } return append([:], [+1:]...) } // Prioritized returns a new PrioritizedValue. func ( interface{}, int) PrioritizedValue { return PrioritizedValue{, } } func bytesHash( []byte) uint64 { var uint64 = 5381 for , := range { = (( << 5) + ) + uint64() } return } // BytesFilter is a efficient data structure for checking whether bytes exist or not. // BytesFilter is thread-safe. type BytesFilter interface { // Add adds given bytes to this set. Add([]byte) // Contains return true if this set contains given bytes, otherwise false. Contains([]byte) bool // Extend copies this filter and adds given bytes to new filter. Extend(...[]byte) BytesFilter } type bytesFilter struct { chars [256]uint8 threshold int slots [][][]byte } // NewBytesFilter returns a new BytesFilter. func ( ...[]byte) BytesFilter { := &bytesFilter{ threshold: 3, slots: make([][][]byte, 64), } for , := range { .Add() } return } func ( *bytesFilter) ( []byte) { := len() := .threshold if < .threshold { = } for := 0; < ; ++ { .chars[[]] |= 1 << uint8() } := bytesHash() % uint64(len(.slots)) := .slots[] if == nil { = [][]byte{} } .slots[] = append(, ) } func ( *bytesFilter) ( ...[]byte) BytesFilter { := NewBytesFilter().(*bytesFilter) .chars = .chars .threshold = .threshold for , := range .slots { := make([][]byte, len()) copy(, ) .slots[] = } for , := range { .Add() } return } func ( *bytesFilter) ( []byte) bool { := len() := .threshold if < .threshold { = } for := 0; < ; ++ { if (.chars[[]] & (1 << uint8())) == 0 { return false } } := bytesHash() % uint64(len(.slots)) := .slots[] if len() == 0 { return false } for , := range { if bytes.Equal(, ) { return true } } return false }