func parseURL(urlStr string) (parsedURL *url.URL, err error) {
	parsedURL = new(url.URL)
	rest, _ := split(urlStr, "#", true)
	rest = strings.TrimSpace(rest)
	rest = strings.Replace(rest, "\t", "", -1)
	rest = strings.Replace(rest, "\r", "", -1)
	rest = strings.Replace(rest, "\n", "", -1)
	rest, err = normalizeEscape(rest)
	if err != nil {
		return nil, err
	}
	parsedURL.Scheme, rest = getScheme(rest)
	rest, parsedURL.RawQuery = split(rest, "?", true)
	var hostish string
	if !strings.HasPrefix(rest, "//") && parsedURL.Scheme != "" {
		return nil, errors.New("safebrowsing: invalid path")
	}
	if parsedURL.Scheme == "" {
		parsedURL.Scheme = "http"
		hostish, rest = split(rest, "/", false)
	} else {
		hostish, rest = split(rest[2:], "/", false)
	}
	if hostish == "" {
		return nil, errors.New("safebrowsing: missing hostname")
	}
	parsedURL.Host, err = parseHost(hostish)
	if err != nil {
		return nil, err
	}
	p := path.Clean(rest)
	if p == "." {
		p = "/"
	} else if rest[len(rest)-1] == '/' && p[len(p)-1] != '/' {
		p += "/"
	}
	parsedURL.Path = p
	return parsedURL, nil
}
parseURL parses urlStr as a url.URL and reports an error if not possible.
package safebrowsing
import (
	"bytes"
	"errors"
	"fmt"
	"net"
	"net/url"
	"path"
	"regexp"
	"strconv"
	"strings"
	"golang.org/x/net/idna"
)
var (
	dotsRegexp          = regexp.MustCompile("[.]+")
	portRegexp          = regexp.MustCompile(`:\d+$`)
	possibleIPRegexp    = regexp.MustCompile(`^(?i)((?:0x[0-9a-f]+|[0-9\.])+)$`)
	trailingSpaceRegexp = regexp.MustCompile(`^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}) `)
)
func ValidURL(url string) bool {
	parsed, err := parseURL(url)
	return parsed != nil && err == nil
}
func generateHashes(url string) (map[hashPrefix]string, error) {
	patterns, err := generatePatterns(url)
	if err != nil {
		return nil, err
	}
	hashes := make(map[hashPrefix]string)
	for _, p := range patterns {
		hashes[hashFromPattern(p)] = p
	}
	return hashes, nil
}
func generatePatterns(url string) ([]string, error) {
	hosts, err := generateLookupHosts(url)
	if err != nil {
		return nil, err
	}
	paths, err := generateLookupPaths(url)
	if err != nil {
		return nil, err
	}
	var patterns []string
	for _, h := range hosts {
		for _, p := range paths {
			patterns = append(patterns, h+p)
		}
	}
	return patterns, nil
}
func isHex(c byte) bool {
	switch {
	case '0' <= c && c <= '9':
		return true
	case 'a' <= c && c <= 'f':
		return true
	case 'A' <= c && c <= 'F':
		return true
	}
	return false
}
func unhex(c byte) byte {
	switch {
	case '0' <= c && c <= '9':
		return c - '0'
	case 'a' <= c && c <= 'f':
		return c - 'a' + 10
	case 'A' <= c && c <= 'F':
		return c - 'A' + 10
	}
	return 0
}
func isUnicode(s string) bool {
	for _, c := range []byte(s) {
		if c > 0x80 {
			return true
		}
	}
	return false
}
func split(s string, c string, cutc bool) (string, string) {
	i := strings.Index(s, c)
	if i < 0 {
		return s, ""
	}
	if cutc {
		return s[:i], s[i+len(c):]
	}
	return s[:i], s[i:]
}
func escape(s string) string {
	var b bytes.Buffer
	for _, c := range []byte(s) {
		if c < 0x20 || c >= 0x7f || c == ' ' || c == '#' || c == '%' {
			b.WriteString(fmt.Sprintf("%%%02x", c))
		} else {
			b.WriteByte(c)
		}
	}
	return b.String()
}
func unescape(s string) string {
	var b bytes.Buffer
	for len(s) > 0 {
		if len(s) >= 3 && s[0] == '%' && isHex(s[1]) && isHex(s[2]) {
			b.WriteByte(unhex(s[1])<<4 | unhex(s[2]))
			s = s[3:]
		} else {
			b.WriteByte(s[0])
			s = s[1:]
		}
	}
	return b.String()
}
func recursiveUnescape(s string) (string, error) {
	const maxDepth = 1024
	for i := 0; i < maxDepth; i++ {
		t := unescape(s)
		if t == s {
			return s, nil
		}
		s = t
	}
	return "", errors.New("safebrowsing: unescaping is too recursive")
}
func normalizeEscape(s string) (string, error) {
	u, err := recursiveUnescape(s)
	if err != nil {
		return "", err
	}
	return escape(u), nil
}
func getScheme(url string) (scheme, path string) {
	for i, c := range []byte(url) {
		switch {
		case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z':
		case '0' <= c && c <= '9' || c == '+' || c == '-' || c == '.':
			if i == 0 {
				return "", url
			}
		case c == ':':
			return url[:i], url[i+1:]
		default:
			return "", url
		}
	}
	return "", url
}
func parseHost(hostish string) (host string, err error) {
	i := strings.LastIndex(hostish, "@")
	if i < 0 {
		host = hostish
	} else {
		host = hostish[i+1:]
	}
	if strings.HasPrefix(host, "[") {
		i := strings.LastIndex(host, "]")
		if i < 0 {
			return "", errors.New("safebrowsing: missing ']' in host")
		}
	}
	host = portRegexp.ReplaceAllString(host, "")
	u := unescape(host)
	if isUnicode(u) {
		host, err = idna.ToASCII(u)
		if err != nil {
			return "", err
		}
	}
	host = dotsRegexp.ReplaceAllString(host, ".")
	host = strings.Trim(host, ".")
	if iphost := parseIPAddress(host); iphost != "" {
		host = iphost
	} else {
		host = strings.ToLower(host)
	}
	return host, nil
}