coverage.out
+ go tool cover -func=coverage.out
+ echo "Coverage profile for ${branch}: ${dir}/coverage.out" >&2
+ popd
+
+ if [ "${branch}" != "HEAD" ]; then
+ git worktree remove --force "$dir"
+ fi
+}
+
+coverage() {
+ case "$1" in
+ -d)
+ shift
+ target="${1?Need to provide a target branch argument}"
+
+ output_dir="$(mktemp -d)"
+ target_out="${output_dir}/target.txt"
+ head_out="${output_dir}/head.txt"
+
+ cover "${target}" > "${target_out}"
+ cover "HEAD" > "${head_out}"
+
+ cat "${target_out}"
+ cat "${head_out}"
+
+ echo ""
+
+ target_pct="$(tail -n2 ${target_out} | head -n1 | sed -E 's/.*total.*\t([0-9.]+)%.*/\1/')"
+ head_pct="$(tail -n2 ${head_out} | head -n1 | sed -E 's/.*total.*\t([0-9.]+)%/\1/')"
+ echo "Results: ${target} ${target_pct}% HEAD ${head_pct}%"
+
+ delta_pct=$(echo "$head_pct - $target_pct" | bc -l)
+ echo "Delta: ${delta_pct}"
+
+ if [[ $delta_pct = \-* ]]; then
+ echo "Regression!";
+
+ target_diff="${output_dir}/target.diff.txt"
+ head_diff="${output_dir}/head.diff.txt"
+ cat "${target_out}" | grep -E '^github.com/pelletier/go-toml' | tr -s "\t " | cut -f 2,3 | sort > "${target_diff}"
+ cat "${head_out}" | grep -E '^github.com/pelletier/go-toml' | tr -s "\t " | cut -f 2,3 | sort > "${head_diff}"
+
+ diff --side-by-side --suppress-common-lines "${target_diff}" "${head_diff}"
+ return 1
+ fi
+ return 0
+ ;;
+ esac
+
+ cover "${1-HEAD}"
+}
+
+bench() {
+ branch="${1}"
+ out="${2}"
+ replace="${3}"
+ dir="$(mktemp -d)"
+
+ stderr "Executing benchmark for ${branch} at ${dir}"
+
+ if [ "${branch}" = "HEAD" ]; then
+ cp -r . "${dir}/"
+ else
+ git worktree add "$dir" "$branch"
+ fi
+
+ pushd "$dir"
+
+ if [ "${replace}" != "" ]; then
+ find ./benchmark/ -iname '*.go' -exec sed -i -E "s|github.com/pelletier/go-toml/v2|${replace}|g" {} \;
+ go get "${replace}"
+ fi
+
+ export GOMAXPROCS=2
+ go test '-bench=^Benchmark(Un)?[mM]arshal' -count=10 -run=Nothing ./... | tee "${out}"
+ popd
+
+ if [ "${branch}" != "HEAD" ]; then
+ git worktree remove --force "$dir"
+ fi
+}
+
+fmktemp() {
+ if mktemp --version &> /dev/null; then
+ # GNU
+ mktemp --suffix=-$1
+ else
+ # BSD
+ mktemp -t $1
+ fi
+}
+
+benchstathtml() {
+python3 - $1 <<'EOF'
+import sys
+
+lines = []
+stop = False
+
+with open(sys.argv[1]) as f:
+ for line in f.readlines():
+ line = line.strip()
+ if line == "":
+ stop = True
+ if not stop:
+ lines.append(line.split(','))
+
+results = []
+for line in reversed(lines[2:]):
+ if len(line) < 8 or line[0] == "":
+ continue
+ v2 = float(line[1])
+ results.append([
+ line[0].replace("-32", ""),
+ "%.1fx" % (float(line[3])/v2), # v1
+ "%.1fx" % (float(line[7])/v2), # bs
+ ])
+# move geomean to the end
+results.append(results[0])
+del results[0]
+
+
+def printtable(data):
+ print("""
+
+
+ | Benchmark | go-toml v1 | BurntSushi/toml |
+
+ """)
+
+ for r in data:
+ print(" | {} | {} | {} |
".format(*r))
+
+ print("""
+
""")
+
+
+def match(x):
+ return "ReferenceFile" in x[0] or "HugoFrontMatter" in x[0]
+
+above = [x for x in results if match(x)]
+below = [x for x in results if not match(x)]
+
+printtable(above)
+print("See more
")
+print("""The table above has the results of the most common use-cases. The table below
+contains the results of all benchmarks, including unrealistic ones. It is
+provided for completeness.
""")
+printtable(below)
+print('This table can be generated with ./ci.sh benchmark -a -html.
')
+print(" ")
+
+EOF
+}
+
+benchmark() {
+ case "$1" in
+ -d)
+ shift
+ target="${1?Need to provide a target branch argument}"
+
+ old=`fmktemp ${target}`
+ bench "${target}" "${old}"
+
+ new=`fmktemp HEAD`
+ bench HEAD "${new}"
+
+ benchstat "${old}" "${new}"
+ return 0
+ ;;
+ -a)
+ shift
+
+ v2stats=`fmktemp go-toml-v2`
+ bench HEAD "${v2stats}" "github.com/pelletier/go-toml/v2"
+ v1stats=`fmktemp go-toml-v1`
+ bench HEAD "${v1stats}" "github.com/pelletier/go-toml"
+ bsstats=`fmktemp bs-toml`
+ bench HEAD "${bsstats}" "github.com/BurntSushi/toml"
+
+ cp "${v2stats}" go-toml-v2.txt
+ cp "${v1stats}" go-toml-v1.txt
+ cp "${bsstats}" bs-toml.txt
+
+ if [ "$1" = "-html" ]; then
+ tmpcsv=`fmktemp csv`
+ benchstat -format csv go-toml-v2.txt go-toml-v1.txt bs-toml.txt > $tmpcsv
+ benchstathtml $tmpcsv
+ else
+ benchstat go-toml-v2.txt go-toml-v1.txt bs-toml.txt
+ fi
+
+ rm -f go-toml-v2.txt go-toml-v1.txt bs-toml.txt
+ return $?
+ esac
+
+ bench "${1-HEAD}" `mktemp`
+}
+
+case "$1" in
+ coverage) shift; coverage $@;;
+ benchmark) shift; benchmark $@;;
+ *) usage "bad argument $1";;
+esac
diff --git a/vendor/github.com/pelletier/go-toml/v2/decode.go b/vendor/github.com/pelletier/go-toml/v2/decode.go
new file mode 100644
index 00000000..f0ec3b17
--- /dev/null
+++ b/vendor/github.com/pelletier/go-toml/v2/decode.go
@@ -0,0 +1,550 @@
+package toml
+
+import (
+ "fmt"
+ "math"
+ "strconv"
+ "time"
+
+ "github.com/pelletier/go-toml/v2/unstable"
+)
+
+func parseInteger(b []byte) (int64, error) {
+ if len(b) > 2 && b[0] == '0' {
+ switch b[1] {
+ case 'x':
+ return parseIntHex(b)
+ case 'b':
+ return parseIntBin(b)
+ case 'o':
+ return parseIntOct(b)
+ default:
+ panic(fmt.Errorf("invalid base '%c', should have been checked by scanIntOrFloat", b[1]))
+ }
+ }
+
+ return parseIntDec(b)
+}
+
+func parseLocalDate(b []byte) (LocalDate, error) {
+ // full-date = date-fullyear "-" date-month "-" date-mday
+ // date-fullyear = 4DIGIT
+ // date-month = 2DIGIT ; 01-12
+ // date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on month/year
+ var date LocalDate
+
+ if len(b) != 10 || b[4] != '-' || b[7] != '-' {
+ return date, unstable.NewParserError(b, "dates are expected to have the format YYYY-MM-DD")
+ }
+
+ var err error
+
+ date.Year, err = parseDecimalDigits(b[0:4])
+ if err != nil {
+ return LocalDate{}, err
+ }
+
+ date.Month, err = parseDecimalDigits(b[5:7])
+ if err != nil {
+ return LocalDate{}, err
+ }
+
+ date.Day, err = parseDecimalDigits(b[8:10])
+ if err != nil {
+ return LocalDate{}, err
+ }
+
+ if !isValidDate(date.Year, date.Month, date.Day) {
+ return LocalDate{}, unstable.NewParserError(b, "impossible date")
+ }
+
+ return date, nil
+}
+
+func parseDecimalDigits(b []byte) (int, error) {
+ v := 0
+
+ for i, c := range b {
+ if c < '0' || c > '9' {
+ return 0, unstable.NewParserError(b[i:i+1], "expected digit (0-9)")
+ }
+ v *= 10
+ v += int(c - '0')
+ }
+
+ return v, nil
+}
+
+func parseDateTime(b []byte) (time.Time, error) {
+ // offset-date-time = full-date time-delim full-time
+ // full-time = partial-time time-offset
+ // time-offset = "Z" / time-numoffset
+ // time-numoffset = ( "+" / "-" ) time-hour ":" time-minute
+
+ dt, b, err := parseLocalDateTime(b)
+ if err != nil {
+ return time.Time{}, err
+ }
+
+ var zone *time.Location
+
+ if len(b) == 0 {
+ // parser should have checked that when assigning the date time node
+ panic("date time should have a timezone")
+ }
+
+ if b[0] == 'Z' || b[0] == 'z' {
+ b = b[1:]
+ zone = time.UTC
+ } else {
+ const dateTimeByteLen = 6
+ if len(b) != dateTimeByteLen {
+ return time.Time{}, unstable.NewParserError(b, "invalid date-time timezone")
+ }
+ var direction int
+ switch b[0] {
+ case '-':
+ direction = -1
+ case '+':
+ direction = +1
+ default:
+ return time.Time{}, unstable.NewParserError(b[:1], "invalid timezone offset character")
+ }
+
+ if b[3] != ':' {
+ return time.Time{}, unstable.NewParserError(b[3:4], "expected a : separator")
+ }
+
+ hours, err := parseDecimalDigits(b[1:3])
+ if err != nil {
+ return time.Time{}, err
+ }
+ if hours > 23 {
+ return time.Time{}, unstable.NewParserError(b[:1], "invalid timezone offset hours")
+ }
+
+ minutes, err := parseDecimalDigits(b[4:6])
+ if err != nil {
+ return time.Time{}, err
+ }
+ if minutes > 59 {
+ return time.Time{}, unstable.NewParserError(b[:1], "invalid timezone offset minutes")
+ }
+
+ seconds := direction * (hours*3600 + minutes*60)
+ if seconds == 0 {
+ zone = time.UTC
+ } else {
+ zone = time.FixedZone("", seconds)
+ }
+ b = b[dateTimeByteLen:]
+ }
+
+ if len(b) > 0 {
+ return time.Time{}, unstable.NewParserError(b, "extra bytes at the end of the timezone")
+ }
+
+ t := time.Date(
+ dt.Year,
+ time.Month(dt.Month),
+ dt.Day,
+ dt.Hour,
+ dt.Minute,
+ dt.Second,
+ dt.Nanosecond,
+ zone)
+
+ return t, nil
+}
+
+func parseLocalDateTime(b []byte) (LocalDateTime, []byte, error) {
+ var dt LocalDateTime
+
+ const localDateTimeByteMinLen = 11
+ if len(b) < localDateTimeByteMinLen {
+ return dt, nil, unstable.NewParserError(b, "local datetimes are expected to have the format YYYY-MM-DDTHH:MM:SS[.NNNNNNNNN]")
+ }
+
+ date, err := parseLocalDate(b[:10])
+ if err != nil {
+ return dt, nil, err
+ }
+ dt.LocalDate = date
+
+ sep := b[10]
+ if sep != 'T' && sep != ' ' && sep != 't' {
+ return dt, nil, unstable.NewParserError(b[10:11], "datetime separator is expected to be T or a space")
+ }
+
+ t, rest, err := parseLocalTime(b[11:])
+ if err != nil {
+ return dt, nil, err
+ }
+ dt.LocalTime = t
+
+ return dt, rest, nil
+}
+
+// parseLocalTime is a bit different because it also returns the remaining
+// []byte that is didn't need. This is to allow parseDateTime to parse those
+// remaining bytes as a timezone.
+func parseLocalTime(b []byte) (LocalTime, []byte, error) {
+ var (
+ nspow = [10]int{0, 1e8, 1e7, 1e6, 1e5, 1e4, 1e3, 1e2, 1e1, 1e0}
+ t LocalTime
+ )
+
+ // check if b matches to have expected format HH:MM:SS[.NNNNNN]
+ const localTimeByteLen = 8
+ if len(b) < localTimeByteLen {
+ return t, nil, unstable.NewParserError(b, "times are expected to have the format HH:MM:SS[.NNNNNN]")
+ }
+
+ var err error
+
+ t.Hour, err = parseDecimalDigits(b[0:2])
+ if err != nil {
+ return t, nil, err
+ }
+
+ if t.Hour > 23 {
+ return t, nil, unstable.NewParserError(b[0:2], "hour cannot be greater 23")
+ }
+ if b[2] != ':' {
+ return t, nil, unstable.NewParserError(b[2:3], "expecting colon between hours and minutes")
+ }
+
+ t.Minute, err = parseDecimalDigits(b[3:5])
+ if err != nil {
+ return t, nil, err
+ }
+ if t.Minute > 59 {
+ return t, nil, unstable.NewParserError(b[3:5], "minutes cannot be greater 59")
+ }
+ if b[5] != ':' {
+ return t, nil, unstable.NewParserError(b[5:6], "expecting colon between minutes and seconds")
+ }
+
+ t.Second, err = parseDecimalDigits(b[6:8])
+ if err != nil {
+ return t, nil, err
+ }
+
+ if t.Second > 60 {
+ return t, nil, unstable.NewParserError(b[6:8], "seconds cannot be greater 60")
+ }
+
+ b = b[8:]
+
+ if len(b) >= 1 && b[0] == '.' {
+ frac := 0
+ precision := 0
+ digits := 0
+
+ for i, c := range b[1:] {
+ if !isDigit(c) {
+ if i == 0 {
+ return t, nil, unstable.NewParserError(b[0:1], "need at least one digit after fraction point")
+ }
+ break
+ }
+ digits++
+
+ const maxFracPrecision = 9
+ if i >= maxFracPrecision {
+ // go-toml allows decoding fractional seconds
+ // beyond the supported precision of 9
+ // digits. It truncates the fractional component
+ // to the supported precision and ignores the
+ // remaining digits.
+ //
+ // https://github.com/pelletier/go-toml/discussions/707
+ continue
+ }
+
+ frac *= 10
+ frac += int(c - '0')
+ precision++
+ }
+
+ if precision == 0 {
+ return t, nil, unstable.NewParserError(b[:1], "nanoseconds need at least one digit")
+ }
+
+ t.Nanosecond = frac * nspow[precision]
+ t.Precision = precision
+
+ return t, b[1+digits:], nil
+ }
+ return t, b, nil
+}
+
+//nolint:cyclop
+func parseFloat(b []byte) (float64, error) {
+ if len(b) == 4 && (b[0] == '+' || b[0] == '-') && b[1] == 'n' && b[2] == 'a' && b[3] == 'n' {
+ return math.NaN(), nil
+ }
+
+ cleaned, err := checkAndRemoveUnderscoresFloats(b)
+ if err != nil {
+ return 0, err
+ }
+
+ if cleaned[0] == '.' {
+ return 0, unstable.NewParserError(b, "float cannot start with a dot")
+ }
+
+ if cleaned[len(cleaned)-1] == '.' {
+ return 0, unstable.NewParserError(b, "float cannot end with a dot")
+ }
+
+ dotAlreadySeen := false
+ for i, c := range cleaned {
+ if c == '.' {
+ if dotAlreadySeen {
+ return 0, unstable.NewParserError(b[i:i+1], "float can have at most one decimal point")
+ }
+ if !isDigit(cleaned[i-1]) {
+ return 0, unstable.NewParserError(b[i-1:i+1], "float decimal point must be preceded by a digit")
+ }
+ if !isDigit(cleaned[i+1]) {
+ return 0, unstable.NewParserError(b[i:i+2], "float decimal point must be followed by a digit")
+ }
+ dotAlreadySeen = true
+ }
+ }
+
+ start := 0
+ if cleaned[0] == '+' || cleaned[0] == '-' {
+ start = 1
+ }
+ if cleaned[start] == '0' && len(cleaned) > start+1 && isDigit(cleaned[start+1]) {
+ return 0, unstable.NewParserError(b, "float integer part cannot have leading zeroes")
+ }
+
+ f, err := strconv.ParseFloat(string(cleaned), 64)
+ if err != nil {
+ return 0, unstable.NewParserError(b, "unable to parse float: %w", err)
+ }
+
+ return f, nil
+}
+
+func parseIntHex(b []byte) (int64, error) {
+ cleaned, err := checkAndRemoveUnderscoresIntegers(b[2:])
+ if err != nil {
+ return 0, err
+ }
+
+ i, err := strconv.ParseInt(string(cleaned), 16, 64)
+ if err != nil {
+ return 0, unstable.NewParserError(b, "couldn't parse hexadecimal number: %w", err)
+ }
+
+ return i, nil
+}
+
+func parseIntOct(b []byte) (int64, error) {
+ cleaned, err := checkAndRemoveUnderscoresIntegers(b[2:])
+ if err != nil {
+ return 0, err
+ }
+
+ i, err := strconv.ParseInt(string(cleaned), 8, 64)
+ if err != nil {
+ return 0, unstable.NewParserError(b, "couldn't parse octal number: %w", err)
+ }
+
+ return i, nil
+}
+
+func parseIntBin(b []byte) (int64, error) {
+ cleaned, err := checkAndRemoveUnderscoresIntegers(b[2:])
+ if err != nil {
+ return 0, err
+ }
+
+ i, err := strconv.ParseInt(string(cleaned), 2, 64)
+ if err != nil {
+ return 0, unstable.NewParserError(b, "couldn't parse binary number: %w", err)
+ }
+
+ return i, nil
+}
+
+func isSign(b byte) bool {
+ return b == '+' || b == '-'
+}
+
+func parseIntDec(b []byte) (int64, error) {
+ cleaned, err := checkAndRemoveUnderscoresIntegers(b)
+ if err != nil {
+ return 0, err
+ }
+
+ startIdx := 0
+
+ if isSign(cleaned[0]) {
+ startIdx++
+ }
+
+ if len(cleaned) > startIdx+1 && cleaned[startIdx] == '0' {
+ return 0, unstable.NewParserError(b, "leading zero not allowed on decimal number")
+ }
+
+ i, err := strconv.ParseInt(string(cleaned), 10, 64)
+ if err != nil {
+ return 0, unstable.NewParserError(b, "couldn't parse decimal number: %w", err)
+ }
+
+ return i, nil
+}
+
+func checkAndRemoveUnderscoresIntegers(b []byte) ([]byte, error) {
+ start := 0
+ if b[start] == '+' || b[start] == '-' {
+ start++
+ }
+
+ if len(b) == start {
+ return b, nil
+ }
+
+ if b[start] == '_' {
+ return nil, unstable.NewParserError(b[start:start+1], "number cannot start with underscore")
+ }
+
+ if b[len(b)-1] == '_' {
+ return nil, unstable.NewParserError(b[len(b)-1:], "number cannot end with underscore")
+ }
+
+ // fast path
+ i := 0
+ for ; i < len(b); i++ {
+ if b[i] == '_' {
+ break
+ }
+ }
+ if i == len(b) {
+ return b, nil
+ }
+
+ before := false
+ cleaned := make([]byte, i, len(b))
+ copy(cleaned, b)
+
+ for i++; i < len(b); i++ {
+ c := b[i]
+ if c == '_' {
+ if !before {
+ return nil, unstable.NewParserError(b[i-1:i+1], "number must have at least one digit between underscores")
+ }
+ before = false
+ } else {
+ before = true
+ cleaned = append(cleaned, c)
+ }
+ }
+
+ return cleaned, nil
+}
+
+func checkAndRemoveUnderscoresFloats(b []byte) ([]byte, error) {
+ if b[0] == '_' {
+ return nil, unstable.NewParserError(b[0:1], "number cannot start with underscore")
+ }
+
+ if b[len(b)-1] == '_' {
+ return nil, unstable.NewParserError(b[len(b)-1:], "number cannot end with underscore")
+ }
+
+ // fast path
+ i := 0
+ for ; i < len(b); i++ {
+ if b[i] == '_' {
+ break
+ }
+ }
+ if i == len(b) {
+ return b, nil
+ }
+
+ before := false
+ cleaned := make([]byte, 0, len(b))
+
+ for i := 0; i < len(b); i++ {
+ c := b[i]
+
+ switch c {
+ case '_':
+ if !before {
+ return nil, unstable.NewParserError(b[i-1:i+1], "number must have at least one digit between underscores")
+ }
+ if i < len(b)-1 && (b[i+1] == 'e' || b[i+1] == 'E') {
+ return nil, unstable.NewParserError(b[i+1:i+2], "cannot have underscore before exponent")
+ }
+ before = false
+ case '+', '-':
+ // signed exponents
+ cleaned = append(cleaned, c)
+ before = false
+ case 'e', 'E':
+ if i < len(b)-1 && b[i+1] == '_' {
+ return nil, unstable.NewParserError(b[i+1:i+2], "cannot have underscore after exponent")
+ }
+ cleaned = append(cleaned, c)
+ case '.':
+ if i < len(b)-1 && b[i+1] == '_' {
+ return nil, unstable.NewParserError(b[i+1:i+2], "cannot have underscore after decimal point")
+ }
+ if i > 0 && b[i-1] == '_' {
+ return nil, unstable.NewParserError(b[i-1:i], "cannot have underscore before decimal point")
+ }
+ cleaned = append(cleaned, c)
+ default:
+ before = true
+ cleaned = append(cleaned, c)
+ }
+ }
+
+ return cleaned, nil
+}
+
+// isValidDate checks if a provided date is a date that exists.
+func isValidDate(year int, month int, day int) bool {
+ return month > 0 && month < 13 && day > 0 && day <= daysIn(month, year)
+}
+
+// daysBefore[m] counts the number of days in a non-leap year
+// before month m begins. There is an entry for m=12, counting
+// the number of days before January of next year (365).
+var daysBefore = [...]int32{
+ 0,
+ 31,
+ 31 + 28,
+ 31 + 28 + 31,
+ 31 + 28 + 31 + 30,
+ 31 + 28 + 31 + 30 + 31,
+ 31 + 28 + 31 + 30 + 31 + 30,
+ 31 + 28 + 31 + 30 + 31 + 30 + 31,
+ 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
+ 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
+ 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
+ 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30,
+ 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31,
+}
+
+func daysIn(m int, year int) int {
+ if m == 2 && isLeap(year) {
+ return 29
+ }
+ return int(daysBefore[m] - daysBefore[m-1])
+}
+
+func isLeap(year int) bool {
+ return year%4 == 0 && (year%100 != 0 || year%400 == 0)
+}
+
+func isDigit(r byte) bool {
+ return r >= '0' && r <= '9'
+}
diff --git a/vendor/github.com/pelletier/go-toml/v2/doc.go b/vendor/github.com/pelletier/go-toml/v2/doc.go
new file mode 100644
index 00000000..b7bc599b
--- /dev/null
+++ b/vendor/github.com/pelletier/go-toml/v2/doc.go
@@ -0,0 +1,2 @@
+// Package toml is a library to read and write TOML documents.
+package toml
diff --git a/vendor/github.com/pelletier/go-toml/v2/errors.go b/vendor/github.com/pelletier/go-toml/v2/errors.go
new file mode 100644
index 00000000..309733f1
--- /dev/null
+++ b/vendor/github.com/pelletier/go-toml/v2/errors.go
@@ -0,0 +1,252 @@
+package toml
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+
+ "github.com/pelletier/go-toml/v2/internal/danger"
+ "github.com/pelletier/go-toml/v2/unstable"
+)
+
+// DecodeError represents an error encountered during the parsing or decoding
+// of a TOML document.
+//
+// In addition to the error message, it contains the position in the document
+// where it happened, as well as a human-readable representation that shows
+// where the error occurred in the document.
+type DecodeError struct {
+ message string
+ line int
+ column int
+ key Key
+
+ human string
+}
+
+// StrictMissingError occurs in a TOML document that does not have a
+// corresponding field in the target value. It contains all the missing fields
+// in Errors.
+//
+// Emitted by Decoder when DisallowUnknownFields() was called.
+type StrictMissingError struct {
+ // One error per field that could not be found.
+ Errors []DecodeError
+}
+
+// Error returns the canonical string for this error.
+func (s *StrictMissingError) Error() string {
+ return "strict mode: fields in the document are missing in the target struct"
+}
+
+// String returns a human readable description of all errors.
+func (s *StrictMissingError) String() string {
+ var buf strings.Builder
+
+ for i, e := range s.Errors {
+ if i > 0 {
+ buf.WriteString("\n---\n")
+ }
+
+ buf.WriteString(e.String())
+ }
+
+ return buf.String()
+}
+
+type Key []string
+
+// Error returns the error message contained in the DecodeError.
+func (e *DecodeError) Error() string {
+ return "toml: " + e.message
+}
+
+// String returns the human-readable contextualized error. This string is multi-line.
+func (e *DecodeError) String() string {
+ return e.human
+}
+
+// Position returns the (line, column) pair indicating where the error
+// occurred in the document. Positions are 1-indexed.
+func (e *DecodeError) Position() (row int, column int) {
+ return e.line, e.column
+}
+
+// Key that was being processed when the error occurred. The key is present only
+// if this DecodeError is part of a StrictMissingError.
+func (e *DecodeError) Key() Key {
+ return e.key
+}
+
+// decodeErrorFromHighlight creates a DecodeError referencing a highlighted
+// range of bytes from document.
+//
+// highlight needs to be a sub-slice of document, or this function panics.
+//
+// The function copies all bytes used in DecodeError, so that document and
+// highlight can be freely deallocated.
+//
+//nolint:funlen
+func wrapDecodeError(document []byte, de *unstable.ParserError) *DecodeError {
+ offset := danger.SubsliceOffset(document, de.Highlight)
+
+ errMessage := de.Error()
+ errLine, errColumn := positionAtEnd(document[:offset])
+ before, after := linesOfContext(document, de.Highlight, offset, 3)
+
+ var buf strings.Builder
+
+ maxLine := errLine + len(after) - 1
+ lineColumnWidth := len(strconv.Itoa(maxLine))
+
+ // Write the lines of context strictly before the error.
+ for i := len(before) - 1; i > 0; i-- {
+ line := errLine - i
+ buf.WriteString(formatLineNumber(line, lineColumnWidth))
+ buf.WriteString("|")
+
+ if len(before[i]) > 0 {
+ buf.WriteString(" ")
+ buf.Write(before[i])
+ }
+
+ buf.WriteRune('\n')
+ }
+
+ // Write the document line that contains the error.
+
+ buf.WriteString(formatLineNumber(errLine, lineColumnWidth))
+ buf.WriteString("| ")
+
+ if len(before) > 0 {
+ buf.Write(before[0])
+ }
+
+ buf.Write(de.Highlight)
+
+ if len(after) > 0 {
+ buf.Write(after[0])
+ }
+
+ buf.WriteRune('\n')
+
+ // Write the line with the error message itself (so it does not have a line
+ // number).
+
+ buf.WriteString(strings.Repeat(" ", lineColumnWidth))
+ buf.WriteString("| ")
+
+ if len(before) > 0 {
+ buf.WriteString(strings.Repeat(" ", len(before[0])))
+ }
+
+ buf.WriteString(strings.Repeat("~", len(de.Highlight)))
+
+ if len(errMessage) > 0 {
+ buf.WriteString(" ")
+ buf.WriteString(errMessage)
+ }
+
+ // Write the lines of context strictly after the error.
+
+ for i := 1; i < len(after); i++ {
+ buf.WriteRune('\n')
+ line := errLine + i
+ buf.WriteString(formatLineNumber(line, lineColumnWidth))
+ buf.WriteString("|")
+
+ if len(after[i]) > 0 {
+ buf.WriteString(" ")
+ buf.Write(after[i])
+ }
+ }
+
+ return &DecodeError{
+ message: errMessage,
+ line: errLine,
+ column: errColumn,
+ key: de.Key,
+ human: buf.String(),
+ }
+}
+
+func formatLineNumber(line int, width int) string {
+ format := "%" + strconv.Itoa(width) + "d"
+
+ return fmt.Sprintf(format, line)
+}
+
+func linesOfContext(document []byte, highlight []byte, offset int, linesAround int) ([][]byte, [][]byte) {
+ return beforeLines(document, offset, linesAround), afterLines(document, highlight, offset, linesAround)
+}
+
+func beforeLines(document []byte, offset int, linesAround int) [][]byte {
+ var beforeLines [][]byte
+
+ // Walk the document backward from the highlight to find previous lines
+ // of context.
+ rest := document[:offset]
+backward:
+ for o := len(rest) - 1; o >= 0 && len(beforeLines) <= linesAround && len(rest) > 0; {
+ switch {
+ case rest[o] == '\n':
+ // handle individual lines
+ beforeLines = append(beforeLines, rest[o+1:])
+ rest = rest[:o]
+ o = len(rest) - 1
+ case o == 0:
+ // add the first line only if it's non-empty
+ beforeLines = append(beforeLines, rest)
+
+ break backward
+ default:
+ o--
+ }
+ }
+
+ return beforeLines
+}
+
+func afterLines(document []byte, highlight []byte, offset int, linesAround int) [][]byte {
+ var afterLines [][]byte
+
+ // Walk the document forward from the highlight to find the following
+ // lines of context.
+ rest := document[offset+len(highlight):]
+forward:
+ for o := 0; o < len(rest) && len(afterLines) <= linesAround; {
+ switch {
+ case rest[o] == '\n':
+ // handle individual lines
+ afterLines = append(afterLines, rest[:o])
+ rest = rest[o+1:]
+ o = 0
+
+ case o == len(rest)-1:
+ // add last line only if it's non-empty
+ afterLines = append(afterLines, rest)
+
+ break forward
+ default:
+ o++
+ }
+ }
+
+ return afterLines
+}
+
+func positionAtEnd(b []byte) (row int, column int) {
+ row = 1
+ column = 1
+
+ for _, c := range b {
+ if c == '\n' {
+ row++
+ column = 1
+ } else {
+ column++
+ }
+ }
+
+ return
+}
diff --git a/vendor/github.com/pelletier/go-toml/v2/internal/characters/ascii.go b/vendor/github.com/pelletier/go-toml/v2/internal/characters/ascii.go
new file mode 100644
index 00000000..80f698db
--- /dev/null
+++ b/vendor/github.com/pelletier/go-toml/v2/internal/characters/ascii.go
@@ -0,0 +1,42 @@
+package characters
+
+var invalidAsciiTable = [256]bool{
+ 0x00: true,
+ 0x01: true,
+ 0x02: true,
+ 0x03: true,
+ 0x04: true,
+ 0x05: true,
+ 0x06: true,
+ 0x07: true,
+ 0x08: true,
+ // 0x09 TAB
+ // 0x0A LF
+ 0x0B: true,
+ 0x0C: true,
+ // 0x0D CR
+ 0x0E: true,
+ 0x0F: true,
+ 0x10: true,
+ 0x11: true,
+ 0x12: true,
+ 0x13: true,
+ 0x14: true,
+ 0x15: true,
+ 0x16: true,
+ 0x17: true,
+ 0x18: true,
+ 0x19: true,
+ 0x1A: true,
+ 0x1B: true,
+ 0x1C: true,
+ 0x1D: true,
+ 0x1E: true,
+ 0x1F: true,
+ // 0x20 - 0x7E Printable ASCII characters
+ 0x7F: true,
+}
+
+func InvalidAscii(b byte) bool {
+ return invalidAsciiTable[b]
+}
diff --git a/vendor/github.com/pelletier/go-toml/v2/internal/characters/utf8.go b/vendor/github.com/pelletier/go-toml/v2/internal/characters/utf8.go
new file mode 100644
index 00000000..db4f45ac
--- /dev/null
+++ b/vendor/github.com/pelletier/go-toml/v2/internal/characters/utf8.go
@@ -0,0 +1,199 @@
+package characters
+
+import (
+ "unicode/utf8"
+)
+
+type utf8Err struct {
+ Index int
+ Size int
+}
+
+func (u utf8Err) Zero() bool {
+ return u.Size == 0
+}
+
+// Verified that a given string is only made of valid UTF-8 characters allowed
+// by the TOML spec:
+//
+// Any Unicode character may be used except those that must be escaped:
+// quotation mark, backslash, and the control characters other than tab (U+0000
+// to U+0008, U+000A to U+001F, U+007F).
+//
+// It is a copy of the Go 1.17 utf8.Valid implementation, tweaked to exit early
+// when a character is not allowed.
+//
+// The returned utf8Err is Zero() if the string is valid, or contains the byte
+// index and size of the invalid character.
+//
+// quotation mark => already checked
+// backslash => already checked
+// 0-0x8 => invalid
+// 0x9 => tab, ok
+// 0xA - 0x1F => invalid
+// 0x7F => invalid
+func Utf8TomlValidAlreadyEscaped(p []byte) (err utf8Err) {
+ // Fast path. Check for and skip 8 bytes of ASCII characters per iteration.
+ offset := 0
+ for len(p) >= 8 {
+ // Combining two 32 bit loads allows the same code to be used
+ // for 32 and 64 bit platforms.
+ // The compiler can generate a 32bit load for first32 and second32
+ // on many platforms. See test/codegen/memcombine.go.
+ first32 := uint32(p[0]) | uint32(p[1])<<8 | uint32(p[2])<<16 | uint32(p[3])<<24
+ second32 := uint32(p[4]) | uint32(p[5])<<8 | uint32(p[6])<<16 | uint32(p[7])<<24
+ if (first32|second32)&0x80808080 != 0 {
+ // Found a non ASCII byte (>= RuneSelf).
+ break
+ }
+
+ for i, b := range p[:8] {
+ if InvalidAscii(b) {
+ err.Index = offset + i
+ err.Size = 1
+ return
+ }
+ }
+
+ p = p[8:]
+ offset += 8
+ }
+ n := len(p)
+ for i := 0; i < n; {
+ pi := p[i]
+ if pi < utf8.RuneSelf {
+ if InvalidAscii(pi) {
+ err.Index = offset + i
+ err.Size = 1
+ return
+ }
+ i++
+ continue
+ }
+ x := first[pi]
+ if x == xx {
+ // Illegal starter byte.
+ err.Index = offset + i
+ err.Size = 1
+ return
+ }
+ size := int(x & 7)
+ if i+size > n {
+ // Short or invalid.
+ err.Index = offset + i
+ err.Size = n - i
+ return
+ }
+ accept := acceptRanges[x>>4]
+ if c := p[i+1]; c < accept.lo || accept.hi < c {
+ err.Index = offset + i
+ err.Size = 2
+ return
+ } else if size == 2 {
+ } else if c := p[i+2]; c < locb || hicb < c {
+ err.Index = offset + i
+ err.Size = 3
+ return
+ } else if size == 3 {
+ } else if c := p[i+3]; c < locb || hicb < c {
+ err.Index = offset + i
+ err.Size = 4
+ return
+ }
+ i += size
+ }
+ return
+}
+
+// Return the size of the next rune if valid, 0 otherwise.
+func Utf8ValidNext(p []byte) int {
+ c := p[0]
+
+ if c < utf8.RuneSelf {
+ if InvalidAscii(c) {
+ return 0
+ }
+ return 1
+ }
+
+ x := first[c]
+ if x == xx {
+ // Illegal starter byte.
+ return 0
+ }
+ size := int(x & 7)
+ if size > len(p) {
+ // Short or invalid.
+ return 0
+ }
+ accept := acceptRanges[x>>4]
+ if c := p[1]; c < accept.lo || accept.hi < c {
+ return 0
+ } else if size == 2 {
+ } else if c := p[2]; c < locb || hicb < c {
+ return 0
+ } else if size == 3 {
+ } else if c := p[3]; c < locb || hicb < c {
+ return 0
+ }
+
+ return size
+}
+
+// acceptRange gives the range of valid values for the second byte in a UTF-8
+// sequence.
+type acceptRange struct {
+ lo uint8 // lowest value for second byte.
+ hi uint8 // highest value for second byte.
+}
+
+// acceptRanges has size 16 to avoid bounds checks in the code that uses it.
+var acceptRanges = [16]acceptRange{
+ 0: {locb, hicb},
+ 1: {0xA0, hicb},
+ 2: {locb, 0x9F},
+ 3: {0x90, hicb},
+ 4: {locb, 0x8F},
+}
+
+// first is information about the first byte in a UTF-8 sequence.
+var first = [256]uint8{
+ // 1 2 3 4 5 6 7 8 9 A B C D E F
+ as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x00-0x0F
+ as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x10-0x1F
+ as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x20-0x2F
+ as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x30-0x3F
+ as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x40-0x4F
+ as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x50-0x5F
+ as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x60-0x6F
+ as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x70-0x7F
+ // 1 2 3 4 5 6 7 8 9 A B C D E F
+ xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0x80-0x8F
+ xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0x90-0x9F
+ xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0xA0-0xAF
+ xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0xB0-0xBF
+ xx, xx, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, // 0xC0-0xCF
+ s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, // 0xD0-0xDF
+ s2, s3, s3, s3, s3, s3, s3, s3, s3, s3, s3, s3, s3, s4, s3, s3, // 0xE0-0xEF
+ s5, s6, s6, s6, s7, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0xF0-0xFF
+}
+
+const (
+ // The default lowest and highest continuation byte.
+ locb = 0b10000000
+ hicb = 0b10111111
+
+ // These names of these constants are chosen to give nice alignment in the
+ // table below. The first nibble is an index into acceptRanges or F for
+ // special one-byte cases. The second nibble is the Rune length or the
+ // Status for the special one-byte case.
+ xx = 0xF1 // invalid: size 1
+ as = 0xF0 // ASCII: size 1
+ s1 = 0x02 // accept 0, size 2
+ s2 = 0x13 // accept 1, size 3
+ s3 = 0x03 // accept 0, size 3
+ s4 = 0x23 // accept 2, size 3
+ s5 = 0x34 // accept 3, size 4
+ s6 = 0x04 // accept 0, size 4
+ s7 = 0x44 // accept 4, size 4
+)
diff --git a/vendor/github.com/pelletier/go-toml/v2/internal/danger/danger.go b/vendor/github.com/pelletier/go-toml/v2/internal/danger/danger.go
new file mode 100644
index 00000000..e38e1131
--- /dev/null
+++ b/vendor/github.com/pelletier/go-toml/v2/internal/danger/danger.go
@@ -0,0 +1,65 @@
+package danger
+
+import (
+ "fmt"
+ "reflect"
+ "unsafe"
+)
+
+const maxInt = uintptr(int(^uint(0) >> 1))
+
+func SubsliceOffset(data []byte, subslice []byte) int {
+ datap := (*reflect.SliceHeader)(unsafe.Pointer(&data))
+ hlp := (*reflect.SliceHeader)(unsafe.Pointer(&subslice))
+
+ if hlp.Data < datap.Data {
+ panic(fmt.Errorf("subslice address (%d) is before data address (%d)", hlp.Data, datap.Data))
+ }
+ offset := hlp.Data - datap.Data
+
+ if offset > maxInt {
+ panic(fmt.Errorf("slice offset larger than int (%d)", offset))
+ }
+
+ intoffset := int(offset)
+
+ if intoffset > datap.Len {
+ panic(fmt.Errorf("slice offset (%d) is farther than data length (%d)", intoffset, datap.Len))
+ }
+
+ if intoffset+hlp.Len > datap.Len {
+ panic(fmt.Errorf("slice ends (%d+%d) is farther than data length (%d)", intoffset, hlp.Len, datap.Len))
+ }
+
+ return intoffset
+}
+
+func BytesRange(start []byte, end []byte) []byte {
+ if start == nil || end == nil {
+ panic("cannot call BytesRange with nil")
+ }
+ startp := (*reflect.SliceHeader)(unsafe.Pointer(&start))
+ endp := (*reflect.SliceHeader)(unsafe.Pointer(&end))
+
+ if startp.Data > endp.Data {
+ panic(fmt.Errorf("start pointer address (%d) is after end pointer address (%d)", startp.Data, endp.Data))
+ }
+
+ l := startp.Len
+ endLen := int(endp.Data-startp.Data) + endp.Len
+ if endLen > l {
+ l = endLen
+ }
+
+ if l > startp.Cap {
+ panic(fmt.Errorf("range length is larger than capacity"))
+ }
+
+ return start[:l]
+}
+
+func Stride(ptr unsafe.Pointer, size uintptr, offset int) unsafe.Pointer {
+ // TODO: replace with unsafe.Add when Go 1.17 is released
+ // https://github.com/golang/go/issues/40481
+ return unsafe.Pointer(uintptr(ptr) + uintptr(int(size)*offset))
+}
diff --git a/vendor/github.com/pelletier/go-toml/v2/internal/danger/typeid.go b/vendor/github.com/pelletier/go-toml/v2/internal/danger/typeid.go
new file mode 100644
index 00000000..9d41c28a
--- /dev/null
+++ b/vendor/github.com/pelletier/go-toml/v2/internal/danger/typeid.go
@@ -0,0 +1,23 @@
+package danger
+
+import (
+ "reflect"
+ "unsafe"
+)
+
+// typeID is used as key in encoder and decoder caches to enable using
+// the optimize runtime.mapaccess2_fast64 function instead of the more
+// expensive lookup if we were to use reflect.Type as map key.
+//
+// typeID holds the pointer to the reflect.Type value, which is unique
+// in the program.
+//
+// https://github.com/segmentio/encoding/blob/master/json/codec.go#L59-L61
+type TypeID unsafe.Pointer
+
+func MakeTypeID(t reflect.Type) TypeID {
+ // reflect.Type has the fields:
+ // typ unsafe.Pointer
+ // ptr unsafe.Pointer
+ return TypeID((*[2]unsafe.Pointer)(unsafe.Pointer(&t))[1])
+}
diff --git a/vendor/github.com/pelletier/go-toml/v2/internal/tracker/key.go b/vendor/github.com/pelletier/go-toml/v2/internal/tracker/key.go
new file mode 100644
index 00000000..149b17f5
--- /dev/null
+++ b/vendor/github.com/pelletier/go-toml/v2/internal/tracker/key.go
@@ -0,0 +1,48 @@
+package tracker
+
+import "github.com/pelletier/go-toml/v2/unstable"
+
+// KeyTracker is a tracker that keeps track of the current Key as the AST is
+// walked.
+type KeyTracker struct {
+ k []string
+}
+
+// UpdateTable sets the state of the tracker with the AST table node.
+func (t *KeyTracker) UpdateTable(node *unstable.Node) {
+ t.reset()
+ t.Push(node)
+}
+
+// UpdateArrayTable sets the state of the tracker with the AST array table node.
+func (t *KeyTracker) UpdateArrayTable(node *unstable.Node) {
+ t.reset()
+ t.Push(node)
+}
+
+// Push the given key on the stack.
+func (t *KeyTracker) Push(node *unstable.Node) {
+ it := node.Key()
+ for it.Next() {
+ t.k = append(t.k, string(it.Node().Data))
+ }
+}
+
+// Pop key from stack.
+func (t *KeyTracker) Pop(node *unstable.Node) {
+ it := node.Key()
+ for it.Next() {
+ t.k = t.k[:len(t.k)-1]
+ }
+}
+
+// Key returns the current key
+func (t *KeyTracker) Key() []string {
+ k := make([]string, len(t.k))
+ copy(k, t.k)
+ return k
+}
+
+func (t *KeyTracker) reset() {
+ t.k = t.k[:0]
+}
diff --git a/vendor/github.com/pelletier/go-toml/v2/internal/tracker/seen.go b/vendor/github.com/pelletier/go-toml/v2/internal/tracker/seen.go
new file mode 100644
index 00000000..76df2d5b
--- /dev/null
+++ b/vendor/github.com/pelletier/go-toml/v2/internal/tracker/seen.go
@@ -0,0 +1,358 @@
+package tracker
+
+import (
+ "bytes"
+ "fmt"
+ "sync"
+
+ "github.com/pelletier/go-toml/v2/unstable"
+)
+
+type keyKind uint8
+
+const (
+ invalidKind keyKind = iota
+ valueKind
+ tableKind
+ arrayTableKind
+)
+
+func (k keyKind) String() string {
+ switch k {
+ case invalidKind:
+ return "invalid"
+ case valueKind:
+ return "value"
+ case tableKind:
+ return "table"
+ case arrayTableKind:
+ return "array table"
+ }
+ panic("missing keyKind string mapping")
+}
+
+// SeenTracker tracks which keys have been seen with which TOML type to flag
+// duplicates and mismatches according to the spec.
+//
+// Each node in the visited tree is represented by an entry. Each entry has an
+// identifier, which is provided by a counter. Entries are stored in the array
+// entries. As new nodes are discovered (referenced for the first time in the
+// TOML document), entries are created and appended to the array. An entry
+// points to its parent using its id.
+//
+// To find whether a given key (sequence of []byte) has already been visited,
+// the entries are linearly searched, looking for one with the right name and
+// parent id.
+//
+// Given that all keys appear in the document after their parent, it is
+// guaranteed that all descendants of a node are stored after the node, this
+// speeds up the search process.
+//
+// When encountering [[array tables]], the descendants of that node are removed
+// to allow that branch of the tree to be "rediscovered". To maintain the
+// invariant above, the deletion process needs to keep the order of entries.
+// This results in more copies in that case.
+type SeenTracker struct {
+ entries []entry
+ currentIdx int
+}
+
+var pool = sync.Pool{
+ New: func() interface{} {
+ return &SeenTracker{}
+ },
+}
+
+func (s *SeenTracker) reset() {
+ // Always contains a root element at index 0.
+ s.currentIdx = 0
+ if len(s.entries) == 0 {
+ s.entries = make([]entry, 1, 2)
+ } else {
+ s.entries = s.entries[:1]
+ }
+ s.entries[0].child = -1
+ s.entries[0].next = -1
+}
+
+type entry struct {
+ // Use -1 to indicate no child or no sibling.
+ child int
+ next int
+
+ name []byte
+ kind keyKind
+ explicit bool
+ kv bool
+}
+
+// Find the index of the child of parentIdx with key k. Returns -1 if
+// it does not exist.
+func (s *SeenTracker) find(parentIdx int, k []byte) int {
+ for i := s.entries[parentIdx].child; i >= 0; i = s.entries[i].next {
+ if bytes.Equal(s.entries[i].name, k) {
+ return i
+ }
+ }
+ return -1
+}
+
+// Remove all descendants of node at position idx.
+func (s *SeenTracker) clear(idx int) {
+ if idx >= len(s.entries) {
+ return
+ }
+
+ for i := s.entries[idx].child; i >= 0; {
+ next := s.entries[i].next
+ n := s.entries[0].next
+ s.entries[0].next = i
+ s.entries[i].next = n
+ s.entries[i].name = nil
+ s.clear(i)
+ i = next
+ }
+
+ s.entries[idx].child = -1
+}
+
+func (s *SeenTracker) create(parentIdx int, name []byte, kind keyKind, explicit bool, kv bool) int {
+ e := entry{
+ child: -1,
+ next: s.entries[parentIdx].child,
+
+ name: name,
+ kind: kind,
+ explicit: explicit,
+ kv: kv,
+ }
+ var idx int
+ if s.entries[0].next >= 0 {
+ idx = s.entries[0].next
+ s.entries[0].next = s.entries[idx].next
+ s.entries[idx] = e
+ } else {
+ idx = len(s.entries)
+ s.entries = append(s.entries, e)
+ }
+
+ s.entries[parentIdx].child = idx
+
+ return idx
+}
+
+func (s *SeenTracker) setExplicitFlag(parentIdx int) {
+ for i := s.entries[parentIdx].child; i >= 0; i = s.entries[i].next {
+ if s.entries[i].kv {
+ s.entries[i].explicit = true
+ s.entries[i].kv = false
+ }
+ s.setExplicitFlag(i)
+ }
+}
+
+// CheckExpression takes a top-level node and checks that it does not contain
+// keys that have been seen in previous calls, and validates that types are
+// consistent. It returns true if it is the first time this node's key is seen.
+// Useful to clear array tables on first use.
+func (s *SeenTracker) CheckExpression(node *unstable.Node) (bool, error) {
+ if s.entries == nil {
+ s.reset()
+ }
+ switch node.Kind {
+ case unstable.KeyValue:
+ return s.checkKeyValue(node)
+ case unstable.Table:
+ return s.checkTable(node)
+ case unstable.ArrayTable:
+ return s.checkArrayTable(node)
+ default:
+ panic(fmt.Errorf("this should not be a top level node type: %s", node.Kind))
+ }
+}
+
+func (s *SeenTracker) checkTable(node *unstable.Node) (bool, error) {
+ if s.currentIdx >= 0 {
+ s.setExplicitFlag(s.currentIdx)
+ }
+
+ it := node.Key()
+
+ parentIdx := 0
+
+ // This code is duplicated in checkArrayTable. This is because factoring
+ // it in a function requires to copy the iterator, or allocate it to the
+ // heap, which is not cheap.
+ for it.Next() {
+ if it.IsLast() {
+ break
+ }
+
+ k := it.Node().Data
+
+ idx := s.find(parentIdx, k)
+
+ if idx < 0 {
+ idx = s.create(parentIdx, k, tableKind, false, false)
+ } else {
+ entry := s.entries[idx]
+ if entry.kind == valueKind {
+ return false, fmt.Errorf("toml: expected %s to be a table, not a %s", string(k), entry.kind)
+ }
+ }
+ parentIdx = idx
+ }
+
+ k := it.Node().Data
+ idx := s.find(parentIdx, k)
+
+ first := false
+ if idx >= 0 {
+ kind := s.entries[idx].kind
+ if kind != tableKind {
+ return false, fmt.Errorf("toml: key %s should be a table, not a %s", string(k), kind)
+ }
+ if s.entries[idx].explicit {
+ return false, fmt.Errorf("toml: table %s already exists", string(k))
+ }
+ s.entries[idx].explicit = true
+ } else {
+ idx = s.create(parentIdx, k, tableKind, true, false)
+ first = true
+ }
+
+ s.currentIdx = idx
+
+ return first, nil
+}
+
+func (s *SeenTracker) checkArrayTable(node *unstable.Node) (bool, error) {
+ if s.currentIdx >= 0 {
+ s.setExplicitFlag(s.currentIdx)
+ }
+
+ it := node.Key()
+
+ parentIdx := 0
+
+ for it.Next() {
+ if it.IsLast() {
+ break
+ }
+
+ k := it.Node().Data
+
+ idx := s.find(parentIdx, k)
+
+ if idx < 0 {
+ idx = s.create(parentIdx, k, tableKind, false, false)
+ } else {
+ entry := s.entries[idx]
+ if entry.kind == valueKind {
+ return false, fmt.Errorf("toml: expected %s to be a table, not a %s", string(k), entry.kind)
+ }
+ }
+
+ parentIdx = idx
+ }
+
+ k := it.Node().Data
+ idx := s.find(parentIdx, k)
+
+ firstTime := idx < 0
+ if firstTime {
+ idx = s.create(parentIdx, k, arrayTableKind, true, false)
+ } else {
+ kind := s.entries[idx].kind
+ if kind != arrayTableKind {
+ return false, fmt.Errorf("toml: key %s already exists as a %s, but should be an array table", kind, string(k))
+ }
+ s.clear(idx)
+ }
+
+ s.currentIdx = idx
+
+ return firstTime, nil
+}
+
+func (s *SeenTracker) checkKeyValue(node *unstable.Node) (bool, error) {
+ parentIdx := s.currentIdx
+ it := node.Key()
+
+ for it.Next() {
+ k := it.Node().Data
+
+ idx := s.find(parentIdx, k)
+
+ if idx < 0 {
+ idx = s.create(parentIdx, k, tableKind, false, true)
+ } else {
+ entry := s.entries[idx]
+ if it.IsLast() {
+ return false, fmt.Errorf("toml: key %s is already defined", string(k))
+ } else if entry.kind != tableKind {
+ return false, fmt.Errorf("toml: expected %s to be a table, not a %s", string(k), entry.kind)
+ } else if entry.explicit {
+ return false, fmt.Errorf("toml: cannot redefine table %s that has already been explicitly defined", string(k))
+ }
+ }
+
+ parentIdx = idx
+ }
+
+ s.entries[parentIdx].kind = valueKind
+
+ value := node.Value()
+
+ switch value.Kind {
+ case unstable.InlineTable:
+ return s.checkInlineTable(value)
+ case unstable.Array:
+ return s.checkArray(value)
+ }
+
+ return false, nil
+}
+
+func (s *SeenTracker) checkArray(node *unstable.Node) (first bool, err error) {
+ it := node.Children()
+ for it.Next() {
+ n := it.Node()
+ switch n.Kind {
+ case unstable.InlineTable:
+ first, err = s.checkInlineTable(n)
+ if err != nil {
+ return false, err
+ }
+ case unstable.Array:
+ first, err = s.checkArray(n)
+ if err != nil {
+ return false, err
+ }
+ }
+ }
+ return first, nil
+}
+
+func (s *SeenTracker) checkInlineTable(node *unstable.Node) (first bool, err error) {
+ s = pool.Get().(*SeenTracker)
+ s.reset()
+
+ it := node.Children()
+ for it.Next() {
+ n := it.Node()
+ first, err = s.checkKeyValue(n)
+ if err != nil {
+ return false, err
+ }
+ }
+
+ // As inline tables are self-contained, the tracker does not
+ // need to retain the details of what they contain. The
+ // keyValue element that creates the inline table is kept to
+ // mark the presence of the inline table and prevent
+ // redefinition of its keys: check* functions cannot walk into
+ // a value.
+ pool.Put(s)
+ return first, nil
+}
diff --git a/vendor/github.com/pelletier/go-toml/v2/internal/tracker/tracker.go b/vendor/github.com/pelletier/go-toml/v2/internal/tracker/tracker.go
new file mode 100644
index 00000000..bf031739
--- /dev/null
+++ b/vendor/github.com/pelletier/go-toml/v2/internal/tracker/tracker.go
@@ -0,0 +1 @@
+package tracker
diff --git a/vendor/github.com/pelletier/go-toml/v2/localtime.go b/vendor/github.com/pelletier/go-toml/v2/localtime.go
new file mode 100644
index 00000000..a856bfdb
--- /dev/null
+++ b/vendor/github.com/pelletier/go-toml/v2/localtime.go
@@ -0,0 +1,122 @@
+package toml
+
+import (
+ "fmt"
+ "strings"
+ "time"
+
+ "github.com/pelletier/go-toml/v2/unstable"
+)
+
+// LocalDate represents a calendar day in no specific timezone.
+type LocalDate struct {
+ Year int
+ Month int
+ Day int
+}
+
+// AsTime converts d into a specific time instance at midnight in zone.
+func (d LocalDate) AsTime(zone *time.Location) time.Time {
+ return time.Date(d.Year, time.Month(d.Month), d.Day, 0, 0, 0, 0, zone)
+}
+
+// String returns RFC 3339 representation of d.
+func (d LocalDate) String() string {
+ return fmt.Sprintf("%04d-%02d-%02d", d.Year, d.Month, d.Day)
+}
+
+// MarshalText returns RFC 3339 representation of d.
+func (d LocalDate) MarshalText() ([]byte, error) {
+ return []byte(d.String()), nil
+}
+
+// UnmarshalText parses b using RFC 3339 to fill d.
+func (d *LocalDate) UnmarshalText(b []byte) error {
+ res, err := parseLocalDate(b)
+ if err != nil {
+ return err
+ }
+ *d = res
+ return nil
+}
+
+// LocalTime represents a time of day of no specific day in no specific
+// timezone.
+type LocalTime struct {
+ Hour int // Hour of the day: [0; 24[
+ Minute int // Minute of the hour: [0; 60[
+ Second int // Second of the minute: [0; 60[
+ Nanosecond int // Nanoseconds within the second: [0, 1000000000[
+ Precision int // Number of digits to display for Nanosecond.
+}
+
+// String returns RFC 3339 representation of d.
+// If d.Nanosecond and d.Precision are zero, the time won't have a nanosecond
+// component. If d.Nanosecond > 0 but d.Precision = 0, then the minimum number
+// of digits for nanoseconds is provided.
+func (d LocalTime) String() string {
+ s := fmt.Sprintf("%02d:%02d:%02d", d.Hour, d.Minute, d.Second)
+
+ if d.Precision > 0 {
+ s += fmt.Sprintf(".%09d", d.Nanosecond)[:d.Precision+1]
+ } else if d.Nanosecond > 0 {
+ // Nanoseconds are specified, but precision is not provided. Use the
+ // minimum.
+ s += strings.Trim(fmt.Sprintf(".%09d", d.Nanosecond), "0")
+ }
+
+ return s
+}
+
+// MarshalText returns RFC 3339 representation of d.
+func (d LocalTime) MarshalText() ([]byte, error) {
+ return []byte(d.String()), nil
+}
+
+// UnmarshalText parses b using RFC 3339 to fill d.
+func (d *LocalTime) UnmarshalText(b []byte) error {
+ res, left, err := parseLocalTime(b)
+ if err == nil && len(left) != 0 {
+ err = unstable.NewParserError(left, "extra characters")
+ }
+ if err != nil {
+ return err
+ }
+ *d = res
+ return nil
+}
+
+// LocalDateTime represents a time of a specific day in no specific timezone.
+type LocalDateTime struct {
+ LocalDate
+ LocalTime
+}
+
+// AsTime converts d into a specific time instance in zone.
+func (d LocalDateTime) AsTime(zone *time.Location) time.Time {
+ return time.Date(d.Year, time.Month(d.Month), d.Day, d.Hour, d.Minute, d.Second, d.Nanosecond, zone)
+}
+
+// String returns RFC 3339 representation of d.
+func (d LocalDateTime) String() string {
+ return d.LocalDate.String() + "T" + d.LocalTime.String()
+}
+
+// MarshalText returns RFC 3339 representation of d.
+func (d LocalDateTime) MarshalText() ([]byte, error) {
+ return []byte(d.String()), nil
+}
+
+// UnmarshalText parses b using RFC 3339 to fill d.
+func (d *LocalDateTime) UnmarshalText(data []byte) error {
+ res, left, err := parseLocalDateTime(data)
+ if err == nil && len(left) != 0 {
+ err = unstable.NewParserError(left, "extra characters")
+ }
+ if err != nil {
+ return err
+ }
+
+ *d = res
+ return nil
+}
diff --git a/vendor/github.com/pelletier/go-toml/v2/marshaler.go b/vendor/github.com/pelletier/go-toml/v2/marshaler.go
new file mode 100644
index 00000000..7f4e20c1
--- /dev/null
+++ b/vendor/github.com/pelletier/go-toml/v2/marshaler.go
@@ -0,0 +1,1121 @@
+package toml
+
+import (
+ "bytes"
+ "encoding"
+ "encoding/json"
+ "fmt"
+ "io"
+ "math"
+ "reflect"
+ "sort"
+ "strconv"
+ "strings"
+ "time"
+ "unicode"
+
+ "github.com/pelletier/go-toml/v2/internal/characters"
+)
+
+// Marshal serializes a Go value as a TOML document.
+//
+// It is a shortcut for Encoder.Encode() with the default options.
+func Marshal(v interface{}) ([]byte, error) {
+ var buf bytes.Buffer
+ enc := NewEncoder(&buf)
+
+ err := enc.Encode(v)
+ if err != nil {
+ return nil, err
+ }
+
+ return buf.Bytes(), nil
+}
+
+// Encoder writes a TOML document to an output stream.
+type Encoder struct {
+ // output
+ w io.Writer
+
+ // global settings
+ tablesInline bool
+ arraysMultiline bool
+ indentSymbol string
+ indentTables bool
+ marshalJsonNumbers bool
+}
+
+// NewEncoder returns a new Encoder that writes to w.
+func NewEncoder(w io.Writer) *Encoder {
+ return &Encoder{
+ w: w,
+ indentSymbol: " ",
+ }
+}
+
+// SetTablesInline forces the encoder to emit all tables inline.
+//
+// This behavior can be controlled on an individual struct field basis with the
+// inline tag:
+//
+// MyField `toml:",inline"`
+func (enc *Encoder) SetTablesInline(inline bool) *Encoder {
+ enc.tablesInline = inline
+ return enc
+}
+
+// SetArraysMultiline forces the encoder to emit all arrays with one element per
+// line.
+//
+// This behavior can be controlled on an individual struct field basis with the multiline tag:
+//
+// MyField `multiline:"true"`
+func (enc *Encoder) SetArraysMultiline(multiline bool) *Encoder {
+ enc.arraysMultiline = multiline
+ return enc
+}
+
+// SetIndentSymbol defines the string that should be used for indentation. The
+// provided string is repeated for each indentation level. Defaults to two
+// spaces.
+func (enc *Encoder) SetIndentSymbol(s string) *Encoder {
+ enc.indentSymbol = s
+ return enc
+}
+
+// SetIndentTables forces the encoder to intent tables and array tables.
+func (enc *Encoder) SetIndentTables(indent bool) *Encoder {
+ enc.indentTables = indent
+ return enc
+}
+
+// SetMarshalJsonNumbers forces the encoder to serialize `json.Number` as a
+// float or integer instead of relying on TextMarshaler to emit a string.
+//
+// *Unstable:* This method does not follow the compatibility guarantees of
+// semver. It can be changed or removed without a new major version being
+// issued.
+func (enc *Encoder) SetMarshalJsonNumbers(indent bool) *Encoder {
+ enc.marshalJsonNumbers = indent
+ return enc
+}
+
+// Encode writes a TOML representation of v to the stream.
+//
+// If v cannot be represented to TOML it returns an error.
+//
+// # Encoding rules
+//
+// A top level slice containing only maps or structs is encoded as [[table
+// array]].
+//
+// All slices not matching rule 1 are encoded as [array]. As a result, any map
+// or struct they contain is encoded as an {inline table}.
+//
+// Nil interfaces and nil pointers are not supported.
+//
+// Keys in key-values always have one part.
+//
+// Intermediate tables are always printed.
+//
+// By default, strings are encoded as literal string, unless they contain either
+// a newline character or a single quote. In that case they are emitted as
+// quoted strings.
+//
+// Unsigned integers larger than math.MaxInt64 cannot be encoded. Doing so
+// results in an error. This rule exists because the TOML specification only
+// requires parsers to support at least the 64 bits integer range. Allowing
+// larger numbers would create non-standard TOML documents, which may not be
+// readable (at best) by other implementations. To encode such numbers, a
+// solution is a custom type that implements encoding.TextMarshaler.
+//
+// When encoding structs, fields are encoded in order of definition, with their
+// exact name.
+//
+// Tables and array tables are separated by empty lines. However, consecutive
+// subtables definitions are not. For example:
+//
+// [top1]
+//
+// [top2]
+// [top2.child1]
+//
+// [[array]]
+//
+// [[array]]
+// [array.child2]
+//
+// # Struct tags
+//
+// The encoding of each public struct field can be customized by the format
+// string in the "toml" key of the struct field's tag. This follows
+// encoding/json's convention. The format string starts with the name of the
+// field, optionally followed by a comma-separated list of options. The name may
+// be empty in order to provide options without overriding the default name.
+//
+// The "multiline" option emits strings as quoted multi-line TOML strings. It
+// has no effect on fields that would not be encoded as strings.
+//
+// The "inline" option turns fields that would be emitted as tables into inline
+// tables instead. It has no effect on other fields.
+//
+// The "omitempty" option prevents empty values or groups from being emitted.
+//
+// The "commented" option prefixes the value and all its children with a comment
+// symbol.
+//
+// In addition to the "toml" tag struct tag, a "comment" tag can be used to emit
+// a TOML comment before the value being annotated. Comments are ignored inside
+// inline tables. For array tables, the comment is only present before the first
+// element of the array.
+func (enc *Encoder) Encode(v interface{}) error {
+ var (
+ b []byte
+ ctx encoderCtx
+ )
+
+ ctx.inline = enc.tablesInline
+
+ if v == nil {
+ return fmt.Errorf("toml: cannot encode a nil interface")
+ }
+
+ b, err := enc.encode(b, ctx, reflect.ValueOf(v))
+ if err != nil {
+ return err
+ }
+
+ _, err = enc.w.Write(b)
+ if err != nil {
+ return fmt.Errorf("toml: cannot write: %w", err)
+ }
+
+ return nil
+}
+
+type valueOptions struct {
+ multiline bool
+ omitempty bool
+ commented bool
+ comment string
+}
+
+type encoderCtx struct {
+ // Current top-level key.
+ parentKey []string
+
+ // Key that should be used for a KV.
+ key string
+ // Extra flag to account for the empty string
+ hasKey bool
+
+ // Set to true to indicate that the encoder is inside a KV, so that all
+ // tables need to be inlined.
+ insideKv bool
+
+ // Set to true to skip the first table header in an array table.
+ skipTableHeader bool
+
+ // Should the next table be encoded as inline
+ inline bool
+
+ // Indentation level
+ indent int
+
+ // Prefix the current value with a comment.
+ commented bool
+
+ // Options coming from struct tags
+ options valueOptions
+}
+
+func (ctx *encoderCtx) shiftKey() {
+ if ctx.hasKey {
+ ctx.parentKey = append(ctx.parentKey, ctx.key)
+ ctx.clearKey()
+ }
+}
+
+func (ctx *encoderCtx) setKey(k string) {
+ ctx.key = k
+ ctx.hasKey = true
+}
+
+func (ctx *encoderCtx) clearKey() {
+ ctx.key = ""
+ ctx.hasKey = false
+}
+
+func (ctx *encoderCtx) isRoot() bool {
+ return len(ctx.parentKey) == 0 && !ctx.hasKey
+}
+
+func (enc *Encoder) encode(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) {
+ i := v.Interface()
+
+ switch x := i.(type) {
+ case time.Time:
+ if x.Nanosecond() > 0 {
+ return x.AppendFormat(b, time.RFC3339Nano), nil
+ }
+ return x.AppendFormat(b, time.RFC3339), nil
+ case LocalTime:
+ return append(b, x.String()...), nil
+ case LocalDate:
+ return append(b, x.String()...), nil
+ case LocalDateTime:
+ return append(b, x.String()...), nil
+ case json.Number:
+ if enc.marshalJsonNumbers {
+ if x == "" { /// Useful zero value.
+ return append(b, "0"...), nil
+ } else if v, err := x.Int64(); err == nil {
+ return enc.encode(b, ctx, reflect.ValueOf(v))
+ } else if f, err := x.Float64(); err == nil {
+ return enc.encode(b, ctx, reflect.ValueOf(f))
+ } else {
+ return nil, fmt.Errorf("toml: unable to convert %q to int64 or float64", x)
+ }
+ }
+ }
+
+ hasTextMarshaler := v.Type().Implements(textMarshalerType)
+ if hasTextMarshaler || (v.CanAddr() && reflect.PtrTo(v.Type()).Implements(textMarshalerType)) {
+ if !hasTextMarshaler {
+ v = v.Addr()
+ }
+
+ if ctx.isRoot() {
+ return nil, fmt.Errorf("toml: type %s implementing the TextMarshaler interface cannot be a root element", v.Type())
+ }
+
+ text, err := v.Interface().(encoding.TextMarshaler).MarshalText()
+ if err != nil {
+ return nil, err
+ }
+
+ b = enc.encodeString(b, string(text), ctx.options)
+
+ return b, nil
+ }
+
+ switch v.Kind() {
+ // containers
+ case reflect.Map:
+ return enc.encodeMap(b, ctx, v)
+ case reflect.Struct:
+ return enc.encodeStruct(b, ctx, v)
+ case reflect.Slice, reflect.Array:
+ return enc.encodeSlice(b, ctx, v)
+ case reflect.Interface:
+ if v.IsNil() {
+ return nil, fmt.Errorf("toml: encoding a nil interface is not supported")
+ }
+
+ return enc.encode(b, ctx, v.Elem())
+ case reflect.Ptr:
+ if v.IsNil() {
+ return enc.encode(b, ctx, reflect.Zero(v.Type().Elem()))
+ }
+
+ return enc.encode(b, ctx, v.Elem())
+
+ // values
+ case reflect.String:
+ b = enc.encodeString(b, v.String(), ctx.options)
+ case reflect.Float32:
+ f := v.Float()
+
+ if math.IsNaN(f) {
+ b = append(b, "nan"...)
+ } else if f > math.MaxFloat32 {
+ b = append(b, "inf"...)
+ } else if f < -math.MaxFloat32 {
+ b = append(b, "-inf"...)
+ } else if math.Trunc(f) == f {
+ b = strconv.AppendFloat(b, f, 'f', 1, 32)
+ } else {
+ b = strconv.AppendFloat(b, f, 'f', -1, 32)
+ }
+ case reflect.Float64:
+ f := v.Float()
+ if math.IsNaN(f) {
+ b = append(b, "nan"...)
+ } else if f > math.MaxFloat64 {
+ b = append(b, "inf"...)
+ } else if f < -math.MaxFloat64 {
+ b = append(b, "-inf"...)
+ } else if math.Trunc(f) == f {
+ b = strconv.AppendFloat(b, f, 'f', 1, 64)
+ } else {
+ b = strconv.AppendFloat(b, f, 'f', -1, 64)
+ }
+ case reflect.Bool:
+ if v.Bool() {
+ b = append(b, "true"...)
+ } else {
+ b = append(b, "false"...)
+ }
+ case reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8, reflect.Uint:
+ x := v.Uint()
+ if x > uint64(math.MaxInt64) {
+ return nil, fmt.Errorf("toml: not encoding uint (%d) greater than max int64 (%d)", x, int64(math.MaxInt64))
+ }
+ b = strconv.AppendUint(b, x, 10)
+ case reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8, reflect.Int:
+ b = strconv.AppendInt(b, v.Int(), 10)
+ default:
+ return nil, fmt.Errorf("toml: cannot encode value of type %s", v.Kind())
+ }
+
+ return b, nil
+}
+
+func isNil(v reflect.Value) bool {
+ switch v.Kind() {
+ case reflect.Ptr, reflect.Interface, reflect.Map:
+ return v.IsNil()
+ default:
+ return false
+ }
+}
+
+func shouldOmitEmpty(options valueOptions, v reflect.Value) bool {
+ return options.omitempty && isEmptyValue(v)
+}
+
+func (enc *Encoder) encodeKv(b []byte, ctx encoderCtx, options valueOptions, v reflect.Value) ([]byte, error) {
+ var err error
+
+ if !ctx.inline {
+ b = enc.encodeComment(ctx.indent, options.comment, b)
+ b = enc.commented(ctx.commented, b)
+ b = enc.indent(ctx.indent, b)
+ }
+
+ b = enc.encodeKey(b, ctx.key)
+ b = append(b, " = "...)
+
+ // create a copy of the context because the value of a KV shouldn't
+ // modify the global context.
+ subctx := ctx
+ subctx.insideKv = true
+ subctx.shiftKey()
+ subctx.options = options
+
+ b, err = enc.encode(b, subctx, v)
+ if err != nil {
+ return nil, err
+ }
+
+ return b, nil
+}
+
+func (enc *Encoder) commented(commented bool, b []byte) []byte {
+ if commented {
+ return append(b, "# "...)
+ }
+ return b
+}
+
+func isEmptyValue(v reflect.Value) bool {
+ switch v.Kind() {
+ case reflect.Struct:
+ return isEmptyStruct(v)
+ case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
+ return v.Len() == 0
+ case reflect.Bool:
+ return !v.Bool()
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return v.Int() == 0
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ return v.Uint() == 0
+ case reflect.Float32, reflect.Float64:
+ return v.Float() == 0
+ case reflect.Interface, reflect.Ptr:
+ return v.IsNil()
+ }
+ return false
+}
+
+func isEmptyStruct(v reflect.Value) bool {
+ // TODO: merge with walkStruct and cache.
+ typ := v.Type()
+ for i := 0; i < typ.NumField(); i++ {
+ fieldType := typ.Field(i)
+
+ // only consider exported fields
+ if fieldType.PkgPath != "" {
+ continue
+ }
+
+ tag := fieldType.Tag.Get("toml")
+
+ // special field name to skip field
+ if tag == "-" {
+ continue
+ }
+
+ f := v.Field(i)
+
+ if !isEmptyValue(f) {
+ return false
+ }
+ }
+
+ return true
+}
+
+const literalQuote = '\''
+
+func (enc *Encoder) encodeString(b []byte, v string, options valueOptions) []byte {
+ if needsQuoting(v) {
+ return enc.encodeQuotedString(options.multiline, b, v)
+ }
+
+ return enc.encodeLiteralString(b, v)
+}
+
+func needsQuoting(v string) bool {
+ // TODO: vectorize
+ for _, b := range []byte(v) {
+ if b == '\'' || b == '\r' || b == '\n' || characters.InvalidAscii(b) {
+ return true
+ }
+ }
+ return false
+}
+
+// caller should have checked that the string does not contain new lines or ' .
+func (enc *Encoder) encodeLiteralString(b []byte, v string) []byte {
+ b = append(b, literalQuote)
+ b = append(b, v...)
+ b = append(b, literalQuote)
+
+ return b
+}
+
+func (enc *Encoder) encodeQuotedString(multiline bool, b []byte, v string) []byte {
+ stringQuote := `"`
+
+ if multiline {
+ stringQuote = `"""`
+ }
+
+ b = append(b, stringQuote...)
+ if multiline {
+ b = append(b, '\n')
+ }
+
+ const (
+ hextable = "0123456789ABCDEF"
+ // U+0000 to U+0008, U+000A to U+001F, U+007F
+ nul = 0x0
+ bs = 0x8
+ lf = 0xa
+ us = 0x1f
+ del = 0x7f
+ )
+
+ for _, r := range []byte(v) {
+ switch r {
+ case '\\':
+ b = append(b, `\\`...)
+ case '"':
+ b = append(b, `\"`...)
+ case '\b':
+ b = append(b, `\b`...)
+ case '\f':
+ b = append(b, `\f`...)
+ case '\n':
+ if multiline {
+ b = append(b, r)
+ } else {
+ b = append(b, `\n`...)
+ }
+ case '\r':
+ b = append(b, `\r`...)
+ case '\t':
+ b = append(b, `\t`...)
+ default:
+ switch {
+ case r >= nul && r <= bs, r >= lf && r <= us, r == del:
+ b = append(b, `\u00`...)
+ b = append(b, hextable[r>>4])
+ b = append(b, hextable[r&0x0f])
+ default:
+ b = append(b, r)
+ }
+ }
+ }
+
+ b = append(b, stringQuote...)
+
+ return b
+}
+
+// caller should have checked that the string is in A-Z / a-z / 0-9 / - / _ .
+func (enc *Encoder) encodeUnquotedKey(b []byte, v string) []byte {
+ return append(b, v...)
+}
+
+func (enc *Encoder) encodeTableHeader(ctx encoderCtx, b []byte) ([]byte, error) {
+ if len(ctx.parentKey) == 0 {
+ return b, nil
+ }
+
+ b = enc.encodeComment(ctx.indent, ctx.options.comment, b)
+
+ b = enc.commented(ctx.commented, b)
+
+ b = enc.indent(ctx.indent, b)
+
+ b = append(b, '[')
+
+ b = enc.encodeKey(b, ctx.parentKey[0])
+
+ for _, k := range ctx.parentKey[1:] {
+ b = append(b, '.')
+ b = enc.encodeKey(b, k)
+ }
+
+ b = append(b, "]\n"...)
+
+ return b, nil
+}
+
+//nolint:cyclop
+func (enc *Encoder) encodeKey(b []byte, k string) []byte {
+ needsQuotation := false
+ cannotUseLiteral := false
+
+ if len(k) == 0 {
+ return append(b, "''"...)
+ }
+
+ for _, c := range k {
+ if (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-' || c == '_' {
+ continue
+ }
+
+ if c == literalQuote {
+ cannotUseLiteral = true
+ }
+
+ needsQuotation = true
+ }
+
+ if needsQuotation && needsQuoting(k) {
+ cannotUseLiteral = true
+ }
+
+ switch {
+ case cannotUseLiteral:
+ return enc.encodeQuotedString(false, b, k)
+ case needsQuotation:
+ return enc.encodeLiteralString(b, k)
+ default:
+ return enc.encodeUnquotedKey(b, k)
+ }
+}
+
+func (enc *Encoder) keyToString(k reflect.Value) (string, error) {
+ keyType := k.Type()
+ switch {
+ case keyType.Kind() == reflect.String:
+ return k.String(), nil
+
+ case keyType.Implements(textMarshalerType):
+ keyB, err := k.Interface().(encoding.TextMarshaler).MarshalText()
+ if err != nil {
+ return "", fmt.Errorf("toml: error marshalling key %v from text: %w", k, err)
+ }
+ return string(keyB), nil
+ }
+ return "", fmt.Errorf("toml: type %s is not supported as a map key", keyType.Kind())
+}
+
+func (enc *Encoder) encodeMap(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) {
+ var (
+ t table
+ emptyValueOptions valueOptions
+ )
+
+ iter := v.MapRange()
+ for iter.Next() {
+ v := iter.Value()
+
+ if isNil(v) {
+ continue
+ }
+
+ k, err := enc.keyToString(iter.Key())
+ if err != nil {
+ return nil, err
+ }
+
+ if willConvertToTableOrArrayTable(ctx, v) {
+ t.pushTable(k, v, emptyValueOptions)
+ } else {
+ t.pushKV(k, v, emptyValueOptions)
+ }
+ }
+
+ sortEntriesByKey(t.kvs)
+ sortEntriesByKey(t.tables)
+
+ return enc.encodeTable(b, ctx, t)
+}
+
+func sortEntriesByKey(e []entry) {
+ sort.Slice(e, func(i, j int) bool {
+ return e[i].Key < e[j].Key
+ })
+}
+
+type entry struct {
+ Key string
+ Value reflect.Value
+ Options valueOptions
+}
+
+type table struct {
+ kvs []entry
+ tables []entry
+}
+
+func (t *table) pushKV(k string, v reflect.Value, options valueOptions) {
+ for _, e := range t.kvs {
+ if e.Key == k {
+ return
+ }
+ }
+
+ t.kvs = append(t.kvs, entry{Key: k, Value: v, Options: options})
+}
+
+func (t *table) pushTable(k string, v reflect.Value, options valueOptions) {
+ for _, e := range t.tables {
+ if e.Key == k {
+ return
+ }
+ }
+ t.tables = append(t.tables, entry{Key: k, Value: v, Options: options})
+}
+
+func walkStruct(ctx encoderCtx, t *table, v reflect.Value) {
+ // TODO: cache this
+ typ := v.Type()
+ for i := 0; i < typ.NumField(); i++ {
+ fieldType := typ.Field(i)
+
+ // only consider exported fields
+ if fieldType.PkgPath != "" {
+ continue
+ }
+
+ tag := fieldType.Tag.Get("toml")
+
+ // special field name to skip field
+ if tag == "-" {
+ continue
+ }
+
+ k, opts := parseTag(tag)
+ if !isValidName(k) {
+ k = ""
+ }
+
+ f := v.Field(i)
+
+ if k == "" {
+ if fieldType.Anonymous {
+ if fieldType.Type.Kind() == reflect.Struct {
+ walkStruct(ctx, t, f)
+ } else if fieldType.Type.Kind() == reflect.Pointer && !f.IsNil() && f.Elem().Kind() == reflect.Struct {
+ walkStruct(ctx, t, f.Elem())
+ }
+ continue
+ } else {
+ k = fieldType.Name
+ }
+ }
+
+ if isNil(f) {
+ continue
+ }
+
+ options := valueOptions{
+ multiline: opts.multiline,
+ omitempty: opts.omitempty,
+ commented: opts.commented,
+ comment: fieldType.Tag.Get("comment"),
+ }
+
+ if opts.inline || !willConvertToTableOrArrayTable(ctx, f) {
+ t.pushKV(k, f, options)
+ } else {
+ t.pushTable(k, f, options)
+ }
+ }
+}
+
+func (enc *Encoder) encodeStruct(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) {
+ var t table
+
+ walkStruct(ctx, &t, v)
+
+ return enc.encodeTable(b, ctx, t)
+}
+
+func (enc *Encoder) encodeComment(indent int, comment string, b []byte) []byte {
+ for len(comment) > 0 {
+ var line string
+ idx := strings.IndexByte(comment, '\n')
+ if idx >= 0 {
+ line = comment[:idx]
+ comment = comment[idx+1:]
+ } else {
+ line = comment
+ comment = ""
+ }
+ b = enc.indent(indent, b)
+ b = append(b, "# "...)
+ b = append(b, line...)
+ b = append(b, '\n')
+ }
+ return b
+}
+
+func isValidName(s string) bool {
+ if s == "" {
+ return false
+ }
+ for _, c := range s {
+ switch {
+ case strings.ContainsRune("!#$%&()*+-./:;<=>?@[]^_{|}~ ", c):
+ // Backslash and quote chars are reserved, but
+ // otherwise any punctuation chars are allowed
+ // in a tag name.
+ case !unicode.IsLetter(c) && !unicode.IsDigit(c):
+ return false
+ }
+ }
+ return true
+}
+
+type tagOptions struct {
+ multiline bool
+ inline bool
+ omitempty bool
+ commented bool
+}
+
+func parseTag(tag string) (string, tagOptions) {
+ opts := tagOptions{}
+
+ idx := strings.Index(tag, ",")
+ if idx == -1 {
+ return tag, opts
+ }
+
+ raw := tag[idx+1:]
+ tag = string(tag[:idx])
+ for raw != "" {
+ var o string
+ i := strings.Index(raw, ",")
+ if i >= 0 {
+ o, raw = raw[:i], raw[i+1:]
+ } else {
+ o, raw = raw, ""
+ }
+ switch o {
+ case "multiline":
+ opts.multiline = true
+ case "inline":
+ opts.inline = true
+ case "omitempty":
+ opts.omitempty = true
+ case "commented":
+ opts.commented = true
+ }
+ }
+
+ return tag, opts
+}
+
+func (enc *Encoder) encodeTable(b []byte, ctx encoderCtx, t table) ([]byte, error) {
+ var err error
+
+ ctx.shiftKey()
+
+ if ctx.insideKv || (ctx.inline && !ctx.isRoot()) {
+ return enc.encodeTableInline(b, ctx, t)
+ }
+
+ if !ctx.skipTableHeader {
+ b, err = enc.encodeTableHeader(ctx, b)
+ if err != nil {
+ return nil, err
+ }
+
+ if enc.indentTables && len(ctx.parentKey) > 0 {
+ ctx.indent++
+ }
+ }
+ ctx.skipTableHeader = false
+
+ hasNonEmptyKV := false
+ for _, kv := range t.kvs {
+ if shouldOmitEmpty(kv.Options, kv.Value) {
+ continue
+ }
+ hasNonEmptyKV = true
+
+ ctx.setKey(kv.Key)
+ ctx2 := ctx
+ ctx2.commented = kv.Options.commented || ctx2.commented
+
+ b, err = enc.encodeKv(b, ctx2, kv.Options, kv.Value)
+ if err != nil {
+ return nil, err
+ }
+
+ b = append(b, '\n')
+ }
+
+ first := true
+ for _, table := range t.tables {
+ if shouldOmitEmpty(table.Options, table.Value) {
+ continue
+ }
+ if first {
+ first = false
+ if hasNonEmptyKV {
+ b = append(b, '\n')
+ }
+ } else {
+ b = append(b, "\n"...)
+ }
+
+ ctx.setKey(table.Key)
+
+ ctx.options = table.Options
+ ctx2 := ctx
+ ctx2.commented = ctx2.commented || ctx.options.commented
+
+ b, err = enc.encode(b, ctx2, table.Value)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ return b, nil
+}
+
+func (enc *Encoder) encodeTableInline(b []byte, ctx encoderCtx, t table) ([]byte, error) {
+ var err error
+
+ b = append(b, '{')
+
+ first := true
+ for _, kv := range t.kvs {
+ if shouldOmitEmpty(kv.Options, kv.Value) {
+ continue
+ }
+
+ if first {
+ first = false
+ } else {
+ b = append(b, `, `...)
+ }
+
+ ctx.setKey(kv.Key)
+
+ b, err = enc.encodeKv(b, ctx, kv.Options, kv.Value)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ if len(t.tables) > 0 {
+ panic("inline table cannot contain nested tables, only key-values")
+ }
+
+ b = append(b, "}"...)
+
+ return b, nil
+}
+
+func willConvertToTable(ctx encoderCtx, v reflect.Value) bool {
+ if !v.IsValid() {
+ return false
+ }
+ if v.Type() == timeType || v.Type().Implements(textMarshalerType) || (v.Kind() != reflect.Ptr && v.CanAddr() && reflect.PtrTo(v.Type()).Implements(textMarshalerType)) {
+ return false
+ }
+
+ t := v.Type()
+ switch t.Kind() {
+ case reflect.Map, reflect.Struct:
+ return !ctx.inline
+ case reflect.Interface:
+ return willConvertToTable(ctx, v.Elem())
+ case reflect.Ptr:
+ if v.IsNil() {
+ return false
+ }
+
+ return willConvertToTable(ctx, v.Elem())
+ default:
+ return false
+ }
+}
+
+func willConvertToTableOrArrayTable(ctx encoderCtx, v reflect.Value) bool {
+ if ctx.insideKv {
+ return false
+ }
+ t := v.Type()
+
+ if t.Kind() == reflect.Interface {
+ return willConvertToTableOrArrayTable(ctx, v.Elem())
+ }
+
+ if t.Kind() == reflect.Slice || t.Kind() == reflect.Array {
+ if v.Len() == 0 {
+ // An empty slice should be a kv = [].
+ return false
+ }
+
+ for i := 0; i < v.Len(); i++ {
+ t := willConvertToTable(ctx, v.Index(i))
+
+ if !t {
+ return false
+ }
+ }
+
+ return true
+ }
+
+ return willConvertToTable(ctx, v)
+}
+
+func (enc *Encoder) encodeSlice(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) {
+ if v.Len() == 0 {
+ b = append(b, "[]"...)
+
+ return b, nil
+ }
+
+ if willConvertToTableOrArrayTable(ctx, v) {
+ return enc.encodeSliceAsArrayTable(b, ctx, v)
+ }
+
+ return enc.encodeSliceAsArray(b, ctx, v)
+}
+
+// caller should have checked that v is a slice that only contains values that
+// encode into tables.
+func (enc *Encoder) encodeSliceAsArrayTable(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) {
+ ctx.shiftKey()
+
+ scratch := make([]byte, 0, 64)
+
+ scratch = enc.commented(ctx.commented, scratch)
+
+ if enc.indentTables {
+ scratch = enc.indent(ctx.indent, scratch)
+ }
+
+ scratch = append(scratch, "[["...)
+
+ for i, k := range ctx.parentKey {
+ if i > 0 {
+ scratch = append(scratch, '.')
+ }
+
+ scratch = enc.encodeKey(scratch, k)
+ }
+
+ scratch = append(scratch, "]]\n"...)
+ ctx.skipTableHeader = true
+
+ b = enc.encodeComment(ctx.indent, ctx.options.comment, b)
+
+ if enc.indentTables {
+ ctx.indent++
+ }
+
+ for i := 0; i < v.Len(); i++ {
+ if i != 0 {
+ b = append(b, "\n"...)
+ }
+
+ b = append(b, scratch...)
+
+ var err error
+ b, err = enc.encode(b, ctx, v.Index(i))
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ return b, nil
+}
+
+func (enc *Encoder) encodeSliceAsArray(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) {
+ multiline := ctx.options.multiline || enc.arraysMultiline
+ separator := ", "
+
+ b = append(b, '[')
+
+ subCtx := ctx
+ subCtx.options = valueOptions{}
+
+ if multiline {
+ separator = ",\n"
+
+ b = append(b, '\n')
+
+ subCtx.indent++
+ }
+
+ var err error
+ first := true
+
+ for i := 0; i < v.Len(); i++ {
+ if first {
+ first = false
+ } else {
+ b = append(b, separator...)
+ }
+
+ if multiline {
+ b = enc.indent(subCtx.indent, b)
+ }
+
+ b, err = enc.encode(b, subCtx, v.Index(i))
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ if multiline {
+ b = append(b, '\n')
+ b = enc.indent(ctx.indent, b)
+ }
+
+ b = append(b, ']')
+
+ return b, nil
+}
+
+func (enc *Encoder) indent(level int, b []byte) []byte {
+ for i := 0; i < level; i++ {
+ b = append(b, enc.indentSymbol...)
+ }
+
+ return b
+}
diff --git a/vendor/github.com/pelletier/go-toml/v2/strict.go b/vendor/github.com/pelletier/go-toml/v2/strict.go
new file mode 100644
index 00000000..802e7e4d
--- /dev/null
+++ b/vendor/github.com/pelletier/go-toml/v2/strict.go
@@ -0,0 +1,107 @@
+package toml
+
+import (
+ "github.com/pelletier/go-toml/v2/internal/danger"
+ "github.com/pelletier/go-toml/v2/internal/tracker"
+ "github.com/pelletier/go-toml/v2/unstable"
+)
+
+type strict struct {
+ Enabled bool
+
+ // Tracks the current key being processed.
+ key tracker.KeyTracker
+
+ missing []unstable.ParserError
+}
+
+func (s *strict) EnterTable(node *unstable.Node) {
+ if !s.Enabled {
+ return
+ }
+
+ s.key.UpdateTable(node)
+}
+
+func (s *strict) EnterArrayTable(node *unstable.Node) {
+ if !s.Enabled {
+ return
+ }
+
+ s.key.UpdateArrayTable(node)
+}
+
+func (s *strict) EnterKeyValue(node *unstable.Node) {
+ if !s.Enabled {
+ return
+ }
+
+ s.key.Push(node)
+}
+
+func (s *strict) ExitKeyValue(node *unstable.Node) {
+ if !s.Enabled {
+ return
+ }
+
+ s.key.Pop(node)
+}
+
+func (s *strict) MissingTable(node *unstable.Node) {
+ if !s.Enabled {
+ return
+ }
+
+ s.missing = append(s.missing, unstable.ParserError{
+ Highlight: keyLocation(node),
+ Message: "missing table",
+ Key: s.key.Key(),
+ })
+}
+
+func (s *strict) MissingField(node *unstable.Node) {
+ if !s.Enabled {
+ return
+ }
+
+ s.missing = append(s.missing, unstable.ParserError{
+ Highlight: keyLocation(node),
+ Message: "missing field",
+ Key: s.key.Key(),
+ })
+}
+
+func (s *strict) Error(doc []byte) error {
+ if !s.Enabled || len(s.missing) == 0 {
+ return nil
+ }
+
+ err := &StrictMissingError{
+ Errors: make([]DecodeError, 0, len(s.missing)),
+ }
+
+ for _, derr := range s.missing {
+ derr := derr
+ err.Errors = append(err.Errors, *wrapDecodeError(doc, &derr))
+ }
+
+ return err
+}
+
+func keyLocation(node *unstable.Node) []byte {
+ k := node.Key()
+
+ hasOne := k.Next()
+ if !hasOne {
+ panic("should not be called with empty key")
+ }
+
+ start := k.Node().Data
+ end := k.Node().Data
+
+ for k.Next() {
+ end = k.Node().Data
+ }
+
+ return danger.BytesRange(start, end)
+}
diff --git a/vendor/github.com/pelletier/go-toml/v2/toml.abnf b/vendor/github.com/pelletier/go-toml/v2/toml.abnf
new file mode 100644
index 00000000..473f3749
--- /dev/null
+++ b/vendor/github.com/pelletier/go-toml/v2/toml.abnf
@@ -0,0 +1,243 @@
+;; This document describes TOML's syntax, using the ABNF format (defined in
+;; RFC 5234 -- https://www.ietf.org/rfc/rfc5234.txt).
+;;
+;; All valid TOML documents will match this description, however certain
+;; invalid documents would need to be rejected as per the semantics described
+;; in the supporting text description.
+
+;; It is possible to try this grammar interactively, using instaparse.
+;; http://instaparse.mojombo.com/
+;;
+;; To do so, in the lower right, click on Options and change `:input-format` to
+;; ':abnf'. Then paste this entire ABNF document into the grammar entry box
+;; (above the options). Then you can type or paste a sample TOML document into
+;; the beige box on the left. Tada!
+
+;; Overall Structure
+
+toml = expression *( newline expression )
+
+expression = ws [ comment ]
+expression =/ ws keyval ws [ comment ]
+expression =/ ws table ws [ comment ]
+
+;; Whitespace
+
+ws = *wschar
+wschar = %x20 ; Space
+wschar =/ %x09 ; Horizontal tab
+
+;; Newline
+
+newline = %x0A ; LF
+newline =/ %x0D.0A ; CRLF
+
+;; Comment
+
+comment-start-symbol = %x23 ; #
+non-ascii = %x80-D7FF / %xE000-10FFFF
+non-eol = %x09 / %x20-7F / non-ascii
+
+comment = comment-start-symbol *non-eol
+
+;; Key-Value pairs
+
+keyval = key keyval-sep val
+
+key = simple-key / dotted-key
+simple-key = quoted-key / unquoted-key
+
+unquoted-key = 1*( ALPHA / DIGIT / %x2D / %x5F ) ; A-Z / a-z / 0-9 / - / _
+quoted-key = basic-string / literal-string
+dotted-key = simple-key 1*( dot-sep simple-key )
+
+dot-sep = ws %x2E ws ; . Period
+keyval-sep = ws %x3D ws ; =
+
+val = string / boolean / array / inline-table / date-time / float / integer
+
+;; String
+
+string = ml-basic-string / basic-string / ml-literal-string / literal-string
+
+;; Basic String
+
+basic-string = quotation-mark *basic-char quotation-mark
+
+quotation-mark = %x22 ; "
+
+basic-char = basic-unescaped / escaped
+basic-unescaped = wschar / %x21 / %x23-5B / %x5D-7E / non-ascii
+escaped = escape escape-seq-char
+
+escape = %x5C ; \
+escape-seq-char = %x22 ; " quotation mark U+0022
+escape-seq-char =/ %x5C ; \ reverse solidus U+005C
+escape-seq-char =/ %x62 ; b backspace U+0008
+escape-seq-char =/ %x66 ; f form feed U+000C
+escape-seq-char =/ %x6E ; n line feed U+000A
+escape-seq-char =/ %x72 ; r carriage return U+000D
+escape-seq-char =/ %x74 ; t tab U+0009
+escape-seq-char =/ %x75 4HEXDIG ; uXXXX U+XXXX
+escape-seq-char =/ %x55 8HEXDIG ; UXXXXXXXX U+XXXXXXXX
+
+;; Multiline Basic String
+
+ml-basic-string = ml-basic-string-delim [ newline ] ml-basic-body
+ ml-basic-string-delim
+ml-basic-string-delim = 3quotation-mark
+ml-basic-body = *mlb-content *( mlb-quotes 1*mlb-content ) [ mlb-quotes ]
+
+mlb-content = mlb-char / newline / mlb-escaped-nl
+mlb-char = mlb-unescaped / escaped
+mlb-quotes = 1*2quotation-mark
+mlb-unescaped = wschar / %x21 / %x23-5B / %x5D-7E / non-ascii
+mlb-escaped-nl = escape ws newline *( wschar / newline )
+
+;; Literal String
+
+literal-string = apostrophe *literal-char apostrophe
+
+apostrophe = %x27 ; ' apostrophe
+
+literal-char = %x09 / %x20-26 / %x28-7E / non-ascii
+
+;; Multiline Literal String
+
+ml-literal-string = ml-literal-string-delim [ newline ] ml-literal-body
+ ml-literal-string-delim
+ml-literal-string-delim = 3apostrophe
+ml-literal-body = *mll-content *( mll-quotes 1*mll-content ) [ mll-quotes ]
+
+mll-content = mll-char / newline
+mll-char = %x09 / %x20-26 / %x28-7E / non-ascii
+mll-quotes = 1*2apostrophe
+
+;; Integer
+
+integer = dec-int / hex-int / oct-int / bin-int
+
+minus = %x2D ; -
+plus = %x2B ; +
+underscore = %x5F ; _
+digit1-9 = %x31-39 ; 1-9
+digit0-7 = %x30-37 ; 0-7
+digit0-1 = %x30-31 ; 0-1
+
+hex-prefix = %x30.78 ; 0x
+oct-prefix = %x30.6F ; 0o
+bin-prefix = %x30.62 ; 0b
+
+dec-int = [ minus / plus ] unsigned-dec-int
+unsigned-dec-int = DIGIT / digit1-9 1*( DIGIT / underscore DIGIT )
+
+hex-int = hex-prefix HEXDIG *( HEXDIG / underscore HEXDIG )
+oct-int = oct-prefix digit0-7 *( digit0-7 / underscore digit0-7 )
+bin-int = bin-prefix digit0-1 *( digit0-1 / underscore digit0-1 )
+
+;; Float
+
+float = float-int-part ( exp / frac [ exp ] )
+float =/ special-float
+
+float-int-part = dec-int
+frac = decimal-point zero-prefixable-int
+decimal-point = %x2E ; .
+zero-prefixable-int = DIGIT *( DIGIT / underscore DIGIT )
+
+exp = "e" float-exp-part
+float-exp-part = [ minus / plus ] zero-prefixable-int
+
+special-float = [ minus / plus ] ( inf / nan )
+inf = %x69.6e.66 ; inf
+nan = %x6e.61.6e ; nan
+
+;; Boolean
+
+boolean = true / false
+
+true = %x74.72.75.65 ; true
+false = %x66.61.6C.73.65 ; false
+
+;; Date and Time (as defined in RFC 3339)
+
+date-time = offset-date-time / local-date-time / local-date / local-time
+
+date-fullyear = 4DIGIT
+date-month = 2DIGIT ; 01-12
+date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on month/year
+time-delim = "T" / %x20 ; T, t, or space
+time-hour = 2DIGIT ; 00-23
+time-minute = 2DIGIT ; 00-59
+time-second = 2DIGIT ; 00-58, 00-59, 00-60 based on leap second rules
+time-secfrac = "." 1*DIGIT
+time-numoffset = ( "+" / "-" ) time-hour ":" time-minute
+time-offset = "Z" / time-numoffset
+
+partial-time = time-hour ":" time-minute ":" time-second [ time-secfrac ]
+full-date = date-fullyear "-" date-month "-" date-mday
+full-time = partial-time time-offset
+
+;; Offset Date-Time
+
+offset-date-time = full-date time-delim full-time
+
+;; Local Date-Time
+
+local-date-time = full-date time-delim partial-time
+
+;; Local Date
+
+local-date = full-date
+
+;; Local Time
+
+local-time = partial-time
+
+;; Array
+
+array = array-open [ array-values ] ws-comment-newline array-close
+
+array-open = %x5B ; [
+array-close = %x5D ; ]
+
+array-values = ws-comment-newline val ws-comment-newline array-sep array-values
+array-values =/ ws-comment-newline val ws-comment-newline [ array-sep ]
+
+array-sep = %x2C ; , Comma
+
+ws-comment-newline = *( wschar / [ comment ] newline )
+
+;; Table
+
+table = std-table / array-table
+
+;; Standard Table
+
+std-table = std-table-open key std-table-close
+
+std-table-open = %x5B ws ; [ Left square bracket
+std-table-close = ws %x5D ; ] Right square bracket
+
+;; Inline Table
+
+inline-table = inline-table-open [ inline-table-keyvals ] inline-table-close
+
+inline-table-open = %x7B ws ; {
+inline-table-close = ws %x7D ; }
+inline-table-sep = ws %x2C ws ; , Comma
+
+inline-table-keyvals = keyval [ inline-table-sep inline-table-keyvals ]
+
+;; Array Table
+
+array-table = array-table-open key array-table-close
+
+array-table-open = %x5B.5B ws ; [[ Double left square bracket
+array-table-close = ws %x5D.5D ; ]] Double right square bracket
+
+;; Built-in ABNF terms, reproduced here for clarity
+
+ALPHA = %x41-5A / %x61-7A ; A-Z / a-z
+DIGIT = %x30-39 ; 0-9
+HEXDIG = DIGIT / "A" / "B" / "C" / "D" / "E" / "F"
diff --git a/vendor/github.com/pelletier/go-toml/v2/types.go b/vendor/github.com/pelletier/go-toml/v2/types.go
new file mode 100644
index 00000000..3c6b8fe5
--- /dev/null
+++ b/vendor/github.com/pelletier/go-toml/v2/types.go
@@ -0,0 +1,14 @@
+package toml
+
+import (
+ "encoding"
+ "reflect"
+ "time"
+)
+
+var timeType = reflect.TypeOf((*time.Time)(nil)).Elem()
+var textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
+var textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
+var mapStringInterfaceType = reflect.TypeOf(map[string]interface{}(nil))
+var sliceInterfaceType = reflect.TypeOf([]interface{}(nil))
+var stringType = reflect.TypeOf("")
diff --git a/vendor/github.com/pelletier/go-toml/v2/unmarshaler.go b/vendor/github.com/pelletier/go-toml/v2/unmarshaler.go
new file mode 100644
index 00000000..98231bae
--- /dev/null
+++ b/vendor/github.com/pelletier/go-toml/v2/unmarshaler.go
@@ -0,0 +1,1311 @@
+package toml
+
+import (
+ "encoding"
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "math"
+ "reflect"
+ "strings"
+ "sync/atomic"
+ "time"
+
+ "github.com/pelletier/go-toml/v2/internal/danger"
+ "github.com/pelletier/go-toml/v2/internal/tracker"
+ "github.com/pelletier/go-toml/v2/unstable"
+)
+
+// Unmarshal deserializes a TOML document into a Go value.
+//
+// It is a shortcut for Decoder.Decode() with the default options.
+func Unmarshal(data []byte, v interface{}) error {
+ p := unstable.Parser{}
+ p.Reset(data)
+ d := decoder{p: &p}
+
+ return d.FromParser(v)
+}
+
+// Decoder reads and decode a TOML document from an input stream.
+type Decoder struct {
+ // input
+ r io.Reader
+
+ // global settings
+ strict bool
+
+ // toggles unmarshaler interface
+ unmarshalerInterface bool
+}
+
+// NewDecoder creates a new Decoder that will read from r.
+func NewDecoder(r io.Reader) *Decoder {
+ return &Decoder{r: r}
+}
+
+// DisallowUnknownFields causes the Decoder to return an error when the
+// destination is a struct and the input contains a key that does not match a
+// non-ignored field.
+//
+// In that case, the Decoder returns a StrictMissingError that can be used to
+// retrieve the individual errors as well as generate a human readable
+// description of the missing fields.
+func (d *Decoder) DisallowUnknownFields() *Decoder {
+ d.strict = true
+ return d
+}
+
+// EnableUnmarshalerInterface allows to enable unmarshaler interface.
+//
+// With this feature enabled, types implementing the unstable/Unmarshaler
+// interface can be decoded from any structure of the document. It allows types
+// that don't have a straightfoward TOML representation to provide their own
+// decoding logic.
+//
+// Currently, types can only decode from a single value. Tables and array tables
+// are not supported.
+//
+// *Unstable:* This method does not follow the compatibility guarantees of
+// semver. It can be changed or removed without a new major version being
+// issued.
+func (d *Decoder) EnableUnmarshalerInterface() *Decoder {
+ d.unmarshalerInterface = true
+ return d
+}
+
+// Decode the whole content of r into v.
+//
+// By default, values in the document that don't exist in the target Go value
+// are ignored. See Decoder.DisallowUnknownFields() to change this behavior.
+//
+// When a TOML local date, time, or date-time is decoded into a time.Time, its
+// value is represented in time.Local timezone. Otherwise the appropriate Local*
+// structure is used. For time values, precision up to the nanosecond is
+// supported by truncating extra digits.
+//
+// Empty tables decoded in an interface{} create an empty initialized
+// map[string]interface{}.
+//
+// Types implementing the encoding.TextUnmarshaler interface are decoded from a
+// TOML string.
+//
+// When decoding a number, go-toml will return an error if the number is out of
+// bounds for the target type (which includes negative numbers when decoding
+// into an unsigned int).
+//
+// If an error occurs while decoding the content of the document, this function
+// returns a toml.DecodeError, providing context about the issue. When using
+// strict mode and a field is missing, a `toml.StrictMissingError` is
+// returned. In any other case, this function returns a standard Go error.
+//
+// # Type mapping
+//
+// List of supported TOML types and their associated accepted Go types:
+//
+// String -> string
+// Integer -> uint*, int*, depending on size
+// Float -> float*, depending on size
+// Boolean -> bool
+// Offset Date-Time -> time.Time
+// Local Date-time -> LocalDateTime, time.Time
+// Local Date -> LocalDate, time.Time
+// Local Time -> LocalTime, time.Time
+// Array -> slice and array, depending on elements types
+// Table -> map and struct
+// Inline Table -> same as Table
+// Array of Tables -> same as Array and Table
+func (d *Decoder) Decode(v interface{}) error {
+ b, err := ioutil.ReadAll(d.r)
+ if err != nil {
+ return fmt.Errorf("toml: %w", err)
+ }
+
+ p := unstable.Parser{}
+ p.Reset(b)
+ dec := decoder{
+ p: &p,
+ strict: strict{
+ Enabled: d.strict,
+ },
+ unmarshalerInterface: d.unmarshalerInterface,
+ }
+
+ return dec.FromParser(v)
+}
+
+type decoder struct {
+ // Which parser instance in use for this decoding session.
+ p *unstable.Parser
+
+ // Flag indicating that the current expression is stashed.
+ // If set to true, calling nextExpr will not actually pull a new expression
+ // but turn off the flag instead.
+ stashedExpr bool
+
+ // Skip expressions until a table is found. This is set to true when a
+ // table could not be created (missing field in map), so all KV expressions
+ // need to be skipped.
+ skipUntilTable bool
+
+ // Flag indicating that the current array/slice table should be cleared because
+ // it is the first encounter of an array table.
+ clearArrayTable bool
+
+ // Tracks position in Go arrays.
+ // This is used when decoding [[array tables]] into Go arrays. Given array
+ // tables are separate TOML expression, we need to keep track of where we
+ // are at in the Go array, as we can't just introspect its size.
+ arrayIndexes map[reflect.Value]int
+
+ // Tracks keys that have been seen, with which type.
+ seen tracker.SeenTracker
+
+ // Strict mode
+ strict strict
+
+ // Flag that enables/disables unmarshaler interface.
+ unmarshalerInterface bool
+
+ // Current context for the error.
+ errorContext *errorContext
+}
+
+type errorContext struct {
+ Struct reflect.Type
+ Field []int
+}
+
+func (d *decoder) typeMismatchError(toml string, target reflect.Type) error {
+ return fmt.Errorf("toml: %s", d.typeMismatchString(toml, target))
+}
+
+func (d *decoder) typeMismatchString(toml string, target reflect.Type) string {
+ if d.errorContext != nil && d.errorContext.Struct != nil {
+ ctx := d.errorContext
+ f := ctx.Struct.FieldByIndex(ctx.Field)
+ return fmt.Sprintf("cannot decode TOML %s into struct field %s.%s of type %s", toml, ctx.Struct, f.Name, f.Type)
+ }
+ return fmt.Sprintf("cannot decode TOML %s into a Go value of type %s", toml, target)
+}
+
+func (d *decoder) expr() *unstable.Node {
+ return d.p.Expression()
+}
+
+func (d *decoder) nextExpr() bool {
+ if d.stashedExpr {
+ d.stashedExpr = false
+ return true
+ }
+ return d.p.NextExpression()
+}
+
+func (d *decoder) stashExpr() {
+ d.stashedExpr = true
+}
+
+func (d *decoder) arrayIndex(shouldAppend bool, v reflect.Value) int {
+ if d.arrayIndexes == nil {
+ d.arrayIndexes = make(map[reflect.Value]int, 1)
+ }
+
+ idx, ok := d.arrayIndexes[v]
+
+ if !ok {
+ d.arrayIndexes[v] = 0
+ } else if shouldAppend {
+ idx++
+ d.arrayIndexes[v] = idx
+ }
+
+ return idx
+}
+
+func (d *decoder) FromParser(v interface{}) error {
+ r := reflect.ValueOf(v)
+ if r.Kind() != reflect.Ptr {
+ return fmt.Errorf("toml: decoding can only be performed into a pointer, not %s", r.Kind())
+ }
+
+ if r.IsNil() {
+ return fmt.Errorf("toml: decoding pointer target cannot be nil")
+ }
+
+ r = r.Elem()
+ if r.Kind() == reflect.Interface && r.IsNil() {
+ newMap := map[string]interface{}{}
+ r.Set(reflect.ValueOf(newMap))
+ }
+
+ err := d.fromParser(r)
+ if err == nil {
+ return d.strict.Error(d.p.Data())
+ }
+
+ var e *unstable.ParserError
+ if errors.As(err, &e) {
+ return wrapDecodeError(d.p.Data(), e)
+ }
+
+ return err
+}
+
+func (d *decoder) fromParser(root reflect.Value) error {
+ for d.nextExpr() {
+ err := d.handleRootExpression(d.expr(), root)
+ if err != nil {
+ return err
+ }
+ }
+
+ return d.p.Error()
+}
+
+/*
+Rules for the unmarshal code:
+
+- The stack is used to keep track of which values need to be set where.
+- handle* functions <=> switch on a given unstable.Kind.
+- unmarshalX* functions need to unmarshal a node of kind X.
+- An "object" is either a struct or a map.
+*/
+
+func (d *decoder) handleRootExpression(expr *unstable.Node, v reflect.Value) error {
+ var x reflect.Value
+ var err error
+ var first bool // used for to clear array tables on first use
+
+ if !(d.skipUntilTable && expr.Kind == unstable.KeyValue) {
+ first, err = d.seen.CheckExpression(expr)
+ if err != nil {
+ return err
+ }
+ }
+
+ switch expr.Kind {
+ case unstable.KeyValue:
+ if d.skipUntilTable {
+ return nil
+ }
+ x, err = d.handleKeyValue(expr, v)
+ case unstable.Table:
+ d.skipUntilTable = false
+ d.strict.EnterTable(expr)
+ x, err = d.handleTable(expr.Key(), v)
+ case unstable.ArrayTable:
+ d.skipUntilTable = false
+ d.strict.EnterArrayTable(expr)
+ d.clearArrayTable = first
+ x, err = d.handleArrayTable(expr.Key(), v)
+ default:
+ panic(fmt.Errorf("parser should not permit expression of kind %s at document root", expr.Kind))
+ }
+
+ if d.skipUntilTable {
+ if expr.Kind == unstable.Table || expr.Kind == unstable.ArrayTable {
+ d.strict.MissingTable(expr)
+ }
+ } else if err == nil && x.IsValid() {
+ v.Set(x)
+ }
+
+ return err
+}
+
+func (d *decoder) handleArrayTable(key unstable.Iterator, v reflect.Value) (reflect.Value, error) {
+ if key.Next() {
+ return d.handleArrayTablePart(key, v)
+ }
+ return d.handleKeyValues(v)
+}
+
+func (d *decoder) handleArrayTableCollectionLast(key unstable.Iterator, v reflect.Value) (reflect.Value, error) {
+ switch v.Kind() {
+ case reflect.Interface:
+ elem := v.Elem()
+ if !elem.IsValid() {
+ elem = reflect.New(sliceInterfaceType).Elem()
+ elem.Set(reflect.MakeSlice(sliceInterfaceType, 0, 16))
+ } else if elem.Kind() == reflect.Slice {
+ if elem.Type() != sliceInterfaceType {
+ elem = reflect.New(sliceInterfaceType).Elem()
+ elem.Set(reflect.MakeSlice(sliceInterfaceType, 0, 16))
+ } else if !elem.CanSet() {
+ nelem := reflect.New(sliceInterfaceType).Elem()
+ nelem.Set(reflect.MakeSlice(sliceInterfaceType, elem.Len(), elem.Cap()))
+ reflect.Copy(nelem, elem)
+ elem = nelem
+ }
+ if d.clearArrayTable && elem.Len() > 0 {
+ elem.SetLen(0)
+ d.clearArrayTable = false
+ }
+ }
+ return d.handleArrayTableCollectionLast(key, elem)
+ case reflect.Ptr:
+ elem := v.Elem()
+ if !elem.IsValid() {
+ ptr := reflect.New(v.Type().Elem())
+ v.Set(ptr)
+ elem = ptr.Elem()
+ }
+
+ elem, err := d.handleArrayTableCollectionLast(key, elem)
+ if err != nil {
+ return reflect.Value{}, err
+ }
+ v.Elem().Set(elem)
+
+ return v, nil
+ case reflect.Slice:
+ if d.clearArrayTable && v.Len() > 0 {
+ v.SetLen(0)
+ d.clearArrayTable = false
+ }
+ elemType := v.Type().Elem()
+ var elem reflect.Value
+ if elemType.Kind() == reflect.Interface {
+ elem = makeMapStringInterface()
+ } else {
+ elem = reflect.New(elemType).Elem()
+ }
+ elem2, err := d.handleArrayTable(key, elem)
+ if err != nil {
+ return reflect.Value{}, err
+ }
+ if elem2.IsValid() {
+ elem = elem2
+ }
+ return reflect.Append(v, elem), nil
+ case reflect.Array:
+ idx := d.arrayIndex(true, v)
+ if idx >= v.Len() {
+ return v, fmt.Errorf("%s at position %d", d.typeMismatchError("array table", v.Type()), idx)
+ }
+ elem := v.Index(idx)
+ _, err := d.handleArrayTable(key, elem)
+ return v, err
+ default:
+ return reflect.Value{}, d.typeMismatchError("array table", v.Type())
+ }
+}
+
+// When parsing an array table expression, each part of the key needs to be
+// evaluated like a normal key, but if it returns a collection, it also needs to
+// point to the last element of the collection. Unless it is the last part of
+// the key, then it needs to create a new element at the end.
+func (d *decoder) handleArrayTableCollection(key unstable.Iterator, v reflect.Value) (reflect.Value, error) {
+ if key.IsLast() {
+ return d.handleArrayTableCollectionLast(key, v)
+ }
+
+ switch v.Kind() {
+ case reflect.Ptr:
+ elem := v.Elem()
+ if !elem.IsValid() {
+ ptr := reflect.New(v.Type().Elem())
+ v.Set(ptr)
+ elem = ptr.Elem()
+ }
+
+ elem, err := d.handleArrayTableCollection(key, elem)
+ if err != nil {
+ return reflect.Value{}, err
+ }
+ if elem.IsValid() {
+ v.Elem().Set(elem)
+ }
+
+ return v, nil
+ case reflect.Slice:
+ elem := v.Index(v.Len() - 1)
+ x, err := d.handleArrayTable(key, elem)
+ if err != nil || d.skipUntilTable {
+ return reflect.Value{}, err
+ }
+ if x.IsValid() {
+ elem.Set(x)
+ }
+
+ return v, err
+ case reflect.Array:
+ idx := d.arrayIndex(false, v)
+ if idx >= v.Len() {
+ return v, fmt.Errorf("%s at position %d", d.typeMismatchError("array table", v.Type()), idx)
+ }
+ elem := v.Index(idx)
+ _, err := d.handleArrayTable(key, elem)
+ return v, err
+ }
+
+ return d.handleArrayTable(key, v)
+}
+
+func (d *decoder) handleKeyPart(key unstable.Iterator, v reflect.Value, nextFn handlerFn, makeFn valueMakerFn) (reflect.Value, error) {
+ var rv reflect.Value
+
+ // First, dispatch over v to make sure it is a valid object.
+ // There is no guarantee over what it could be.
+ switch v.Kind() {
+ case reflect.Ptr:
+ elem := v.Elem()
+ if !elem.IsValid() {
+ v.Set(reflect.New(v.Type().Elem()))
+ }
+ elem = v.Elem()
+ return d.handleKeyPart(key, elem, nextFn, makeFn)
+ case reflect.Map:
+ vt := v.Type()
+
+ // Create the key for the map element. Convert to key type.
+ mk, err := d.keyFromData(vt.Key(), key.Node().Data)
+ if err != nil {
+ return reflect.Value{}, err
+ }
+
+ // If the map does not exist, create it.
+ if v.IsNil() {
+ vt := v.Type()
+ v = reflect.MakeMap(vt)
+ rv = v
+ }
+
+ mv := v.MapIndex(mk)
+ set := false
+ if !mv.IsValid() {
+ // If there is no value in the map, create a new one according to
+ // the map type. If the element type is interface, create either a
+ // map[string]interface{} or a []interface{} depending on whether
+ // this is the last part of the array table key.
+
+ t := vt.Elem()
+ if t.Kind() == reflect.Interface {
+ mv = makeFn()
+ } else {
+ mv = reflect.New(t).Elem()
+ }
+ set = true
+ } else if mv.Kind() == reflect.Interface {
+ mv = mv.Elem()
+ if !mv.IsValid() {
+ mv = makeFn()
+ }
+ set = true
+ } else if !mv.CanAddr() {
+ vt := v.Type()
+ t := vt.Elem()
+ oldmv := mv
+ mv = reflect.New(t).Elem()
+ mv.Set(oldmv)
+ set = true
+ }
+
+ x, err := nextFn(key, mv)
+ if err != nil {
+ return reflect.Value{}, err
+ }
+
+ if x.IsValid() {
+ mv = x
+ set = true
+ }
+
+ if set {
+ v.SetMapIndex(mk, mv)
+ }
+ case reflect.Struct:
+ path, found := structFieldPath(v, string(key.Node().Data))
+ if !found {
+ d.skipUntilTable = true
+ return reflect.Value{}, nil
+ }
+
+ if d.errorContext == nil {
+ d.errorContext = new(errorContext)
+ }
+ t := v.Type()
+ d.errorContext.Struct = t
+ d.errorContext.Field = path
+
+ f := fieldByIndex(v, path)
+ x, err := nextFn(key, f)
+ if err != nil || d.skipUntilTable {
+ return reflect.Value{}, err
+ }
+ if x.IsValid() {
+ f.Set(x)
+ }
+ d.errorContext.Field = nil
+ d.errorContext.Struct = nil
+ case reflect.Interface:
+ if v.Elem().IsValid() {
+ v = v.Elem()
+ } else {
+ v = makeMapStringInterface()
+ }
+
+ x, err := d.handleKeyPart(key, v, nextFn, makeFn)
+ if err != nil {
+ return reflect.Value{}, err
+ }
+ if x.IsValid() {
+ v = x
+ }
+ rv = v
+ default:
+ panic(fmt.Errorf("unhandled part: %s", v.Kind()))
+ }
+
+ return rv, nil
+}
+
+// HandleArrayTablePart navigates the Go structure v using the key v. It is
+// only used for the prefix (non-last) parts of an array-table. When
+// encountering a collection, it should go to the last element.
+func (d *decoder) handleArrayTablePart(key unstable.Iterator, v reflect.Value) (reflect.Value, error) {
+ var makeFn valueMakerFn
+ if key.IsLast() {
+ makeFn = makeSliceInterface
+ } else {
+ makeFn = makeMapStringInterface
+ }
+ return d.handleKeyPart(key, v, d.handleArrayTableCollection, makeFn)
+}
+
+// HandleTable returns a reference when it has checked the next expression but
+// cannot handle it.
+func (d *decoder) handleTable(key unstable.Iterator, v reflect.Value) (reflect.Value, error) {
+ if v.Kind() == reflect.Slice {
+ if v.Len() == 0 {
+ return reflect.Value{}, unstable.NewParserError(key.Node().Data, "cannot store a table in a slice")
+ }
+ elem := v.Index(v.Len() - 1)
+ x, err := d.handleTable(key, elem)
+ if err != nil {
+ return reflect.Value{}, err
+ }
+ if x.IsValid() {
+ elem.Set(x)
+ }
+ return reflect.Value{}, nil
+ }
+ if key.Next() {
+ // Still scoping the key
+ return d.handleTablePart(key, v)
+ }
+ // Done scoping the key.
+ // Now handle all the key-value expressions in this table.
+ return d.handleKeyValues(v)
+}
+
+// Handle root expressions until the end of the document or the next
+// non-key-value.
+func (d *decoder) handleKeyValues(v reflect.Value) (reflect.Value, error) {
+ var rv reflect.Value
+ for d.nextExpr() {
+ expr := d.expr()
+ if expr.Kind != unstable.KeyValue {
+ // Stash the expression so that fromParser can just loop and use
+ // the right handler.
+ // We could just recurse ourselves here, but at least this gives a
+ // chance to pop the stack a bit.
+ d.stashExpr()
+ break
+ }
+
+ _, err := d.seen.CheckExpression(expr)
+ if err != nil {
+ return reflect.Value{}, err
+ }
+
+ x, err := d.handleKeyValue(expr, v)
+ if err != nil {
+ return reflect.Value{}, err
+ }
+ if x.IsValid() {
+ v = x
+ rv = x
+ }
+ }
+ return rv, nil
+}
+
+type (
+ handlerFn func(key unstable.Iterator, v reflect.Value) (reflect.Value, error)
+ valueMakerFn func() reflect.Value
+)
+
+func makeMapStringInterface() reflect.Value {
+ return reflect.MakeMap(mapStringInterfaceType)
+}
+
+func makeSliceInterface() reflect.Value {
+ return reflect.MakeSlice(sliceInterfaceType, 0, 16)
+}
+
+func (d *decoder) handleTablePart(key unstable.Iterator, v reflect.Value) (reflect.Value, error) {
+ return d.handleKeyPart(key, v, d.handleTable, makeMapStringInterface)
+}
+
+func (d *decoder) tryTextUnmarshaler(node *unstable.Node, v reflect.Value) (bool, error) {
+ // Special case for time, because we allow to unmarshal to it from
+ // different kind of AST nodes.
+ if v.Type() == timeType {
+ return false, nil
+ }
+
+ if v.CanAddr() && v.Addr().Type().Implements(textUnmarshalerType) {
+ err := v.Addr().Interface().(encoding.TextUnmarshaler).UnmarshalText(node.Data)
+ if err != nil {
+ return false, unstable.NewParserError(d.p.Raw(node.Raw), "%w", err)
+ }
+
+ return true, nil
+ }
+
+ return false, nil
+}
+
+func (d *decoder) handleValue(value *unstable.Node, v reflect.Value) error {
+ for v.Kind() == reflect.Ptr {
+ v = initAndDereferencePointer(v)
+ }
+
+ if d.unmarshalerInterface {
+ if v.CanAddr() && v.Addr().CanInterface() {
+ if outi, ok := v.Addr().Interface().(unstable.Unmarshaler); ok {
+ return outi.UnmarshalTOML(value)
+ }
+ }
+ }
+
+ ok, err := d.tryTextUnmarshaler(value, v)
+ if ok || err != nil {
+ return err
+ }
+
+ switch value.Kind {
+ case unstable.String:
+ return d.unmarshalString(value, v)
+ case unstable.Integer:
+ return d.unmarshalInteger(value, v)
+ case unstable.Float:
+ return d.unmarshalFloat(value, v)
+ case unstable.Bool:
+ return d.unmarshalBool(value, v)
+ case unstable.DateTime:
+ return d.unmarshalDateTime(value, v)
+ case unstable.LocalDate:
+ return d.unmarshalLocalDate(value, v)
+ case unstable.LocalTime:
+ return d.unmarshalLocalTime(value, v)
+ case unstable.LocalDateTime:
+ return d.unmarshalLocalDateTime(value, v)
+ case unstable.InlineTable:
+ return d.unmarshalInlineTable(value, v)
+ case unstable.Array:
+ return d.unmarshalArray(value, v)
+ default:
+ panic(fmt.Errorf("handleValue not implemented for %s", value.Kind))
+ }
+}
+
+func (d *decoder) unmarshalArray(array *unstable.Node, v reflect.Value) error {
+ switch v.Kind() {
+ case reflect.Slice:
+ if v.IsNil() {
+ v.Set(reflect.MakeSlice(v.Type(), 0, 16))
+ } else {
+ v.SetLen(0)
+ }
+ case reflect.Array:
+ // arrays are always initialized
+ case reflect.Interface:
+ elem := v.Elem()
+ if !elem.IsValid() {
+ elem = reflect.New(sliceInterfaceType).Elem()
+ elem.Set(reflect.MakeSlice(sliceInterfaceType, 0, 16))
+ } else if elem.Kind() == reflect.Slice {
+ if elem.Type() != sliceInterfaceType {
+ elem = reflect.New(sliceInterfaceType).Elem()
+ elem.Set(reflect.MakeSlice(sliceInterfaceType, 0, 16))
+ } else if !elem.CanSet() {
+ nelem := reflect.New(sliceInterfaceType).Elem()
+ nelem.Set(reflect.MakeSlice(sliceInterfaceType, elem.Len(), elem.Cap()))
+ reflect.Copy(nelem, elem)
+ elem = nelem
+ }
+ }
+ err := d.unmarshalArray(array, elem)
+ if err != nil {
+ return err
+ }
+ v.Set(elem)
+ return nil
+ default:
+ // TODO: use newDecodeError, but first the parser needs to fill
+ // array.Data.
+ return d.typeMismatchError("array", v.Type())
+ }
+
+ elemType := v.Type().Elem()
+
+ it := array.Children()
+ idx := 0
+ for it.Next() {
+ n := it.Node()
+
+ // TODO: optimize
+ if v.Kind() == reflect.Slice {
+ elem := reflect.New(elemType).Elem()
+
+ err := d.handleValue(n, elem)
+ if err != nil {
+ return err
+ }
+
+ v.Set(reflect.Append(v, elem))
+ } else { // array
+ if idx >= v.Len() {
+ return nil
+ }
+ elem := v.Index(idx)
+ err := d.handleValue(n, elem)
+ if err != nil {
+ return err
+ }
+ idx++
+ }
+ }
+
+ return nil
+}
+
+func (d *decoder) unmarshalInlineTable(itable *unstable.Node, v reflect.Value) error {
+ // Make sure v is an initialized object.
+ switch v.Kind() {
+ case reflect.Map:
+ if v.IsNil() {
+ v.Set(reflect.MakeMap(v.Type()))
+ }
+ case reflect.Struct:
+ // structs are always initialized.
+ case reflect.Interface:
+ elem := v.Elem()
+ if !elem.IsValid() {
+ elem = makeMapStringInterface()
+ v.Set(elem)
+ }
+ return d.unmarshalInlineTable(itable, elem)
+ default:
+ return unstable.NewParserError(d.p.Raw(itable.Raw), "cannot store inline table in Go type %s", v.Kind())
+ }
+
+ it := itable.Children()
+ for it.Next() {
+ n := it.Node()
+
+ x, err := d.handleKeyValue(n, v)
+ if err != nil {
+ return err
+ }
+ if x.IsValid() {
+ v = x
+ }
+ }
+
+ return nil
+}
+
+func (d *decoder) unmarshalDateTime(value *unstable.Node, v reflect.Value) error {
+ dt, err := parseDateTime(value.Data)
+ if err != nil {
+ return err
+ }
+
+ v.Set(reflect.ValueOf(dt))
+ return nil
+}
+
+func (d *decoder) unmarshalLocalDate(value *unstable.Node, v reflect.Value) error {
+ ld, err := parseLocalDate(value.Data)
+ if err != nil {
+ return err
+ }
+
+ if v.Type() == timeType {
+ cast := ld.AsTime(time.Local)
+ v.Set(reflect.ValueOf(cast))
+ return nil
+ }
+
+ v.Set(reflect.ValueOf(ld))
+
+ return nil
+}
+
+func (d *decoder) unmarshalLocalTime(value *unstable.Node, v reflect.Value) error {
+ lt, rest, err := parseLocalTime(value.Data)
+ if err != nil {
+ return err
+ }
+
+ if len(rest) > 0 {
+ return unstable.NewParserError(rest, "extra characters at the end of a local time")
+ }
+
+ v.Set(reflect.ValueOf(lt))
+ return nil
+}
+
+func (d *decoder) unmarshalLocalDateTime(value *unstable.Node, v reflect.Value) error {
+ ldt, rest, err := parseLocalDateTime(value.Data)
+ if err != nil {
+ return err
+ }
+
+ if len(rest) > 0 {
+ return unstable.NewParserError(rest, "extra characters at the end of a local date time")
+ }
+
+ if v.Type() == timeType {
+ cast := ldt.AsTime(time.Local)
+
+ v.Set(reflect.ValueOf(cast))
+ return nil
+ }
+
+ v.Set(reflect.ValueOf(ldt))
+
+ return nil
+}
+
+func (d *decoder) unmarshalBool(value *unstable.Node, v reflect.Value) error {
+ b := value.Data[0] == 't'
+
+ switch v.Kind() {
+ case reflect.Bool:
+ v.SetBool(b)
+ case reflect.Interface:
+ v.Set(reflect.ValueOf(b))
+ default:
+ return unstable.NewParserError(value.Data, "cannot assign boolean to a %t", b)
+ }
+
+ return nil
+}
+
+func (d *decoder) unmarshalFloat(value *unstable.Node, v reflect.Value) error {
+ f, err := parseFloat(value.Data)
+ if err != nil {
+ return err
+ }
+
+ switch v.Kind() {
+ case reflect.Float64:
+ v.SetFloat(f)
+ case reflect.Float32:
+ if f > math.MaxFloat32 {
+ return unstable.NewParserError(value.Data, "number %f does not fit in a float32", f)
+ }
+ v.SetFloat(f)
+ case reflect.Interface:
+ v.Set(reflect.ValueOf(f))
+ default:
+ return unstable.NewParserError(value.Data, "float cannot be assigned to %s", v.Kind())
+ }
+
+ return nil
+}
+
+const (
+ maxInt = int64(^uint(0) >> 1)
+ minInt = -maxInt - 1
+)
+
+// Maximum value of uint for decoding. Currently the decoder parses the integer
+// into an int64. As a result, on architectures where uint is 64 bits, the
+// effective maximum uint we can decode is the maximum of int64. On
+// architectures where uint is 32 bits, the maximum value we can decode is
+// lower: the maximum of uint32. I didn't find a way to figure out this value at
+// compile time, so it is computed during initialization.
+var maxUint int64 = math.MaxInt64
+
+func init() {
+ m := uint64(^uint(0))
+ if m < uint64(maxUint) {
+ maxUint = int64(m)
+ }
+}
+
+func (d *decoder) unmarshalInteger(value *unstable.Node, v reflect.Value) error {
+ kind := v.Kind()
+ if kind == reflect.Float32 || kind == reflect.Float64 {
+ return d.unmarshalFloat(value, v)
+ }
+
+ i, err := parseInteger(value.Data)
+ if err != nil {
+ return err
+ }
+
+ var r reflect.Value
+
+ switch kind {
+ case reflect.Int64:
+ v.SetInt(i)
+ return nil
+ case reflect.Int32:
+ if i < math.MinInt32 || i > math.MaxInt32 {
+ return fmt.Errorf("toml: number %d does not fit in an int32", i)
+ }
+
+ r = reflect.ValueOf(int32(i))
+ case reflect.Int16:
+ if i < math.MinInt16 || i > math.MaxInt16 {
+ return fmt.Errorf("toml: number %d does not fit in an int16", i)
+ }
+
+ r = reflect.ValueOf(int16(i))
+ case reflect.Int8:
+ if i < math.MinInt8 || i > math.MaxInt8 {
+ return fmt.Errorf("toml: number %d does not fit in an int8", i)
+ }
+
+ r = reflect.ValueOf(int8(i))
+ case reflect.Int:
+ if i < minInt || i > maxInt {
+ return fmt.Errorf("toml: number %d does not fit in an int", i)
+ }
+
+ r = reflect.ValueOf(int(i))
+ case reflect.Uint64:
+ if i < 0 {
+ return fmt.Errorf("toml: negative number %d does not fit in an uint64", i)
+ }
+
+ r = reflect.ValueOf(uint64(i))
+ case reflect.Uint32:
+ if i < 0 || i > math.MaxUint32 {
+ return fmt.Errorf("toml: negative number %d does not fit in an uint32", i)
+ }
+
+ r = reflect.ValueOf(uint32(i))
+ case reflect.Uint16:
+ if i < 0 || i > math.MaxUint16 {
+ return fmt.Errorf("toml: negative number %d does not fit in an uint16", i)
+ }
+
+ r = reflect.ValueOf(uint16(i))
+ case reflect.Uint8:
+ if i < 0 || i > math.MaxUint8 {
+ return fmt.Errorf("toml: negative number %d does not fit in an uint8", i)
+ }
+
+ r = reflect.ValueOf(uint8(i))
+ case reflect.Uint:
+ if i < 0 || i > maxUint {
+ return fmt.Errorf("toml: negative number %d does not fit in an uint", i)
+ }
+
+ r = reflect.ValueOf(uint(i))
+ case reflect.Interface:
+ r = reflect.ValueOf(i)
+ default:
+ return unstable.NewParserError(d.p.Raw(value.Raw), d.typeMismatchString("integer", v.Type()))
+ }
+
+ if !r.Type().AssignableTo(v.Type()) {
+ r = r.Convert(v.Type())
+ }
+
+ v.Set(r)
+
+ return nil
+}
+
+func (d *decoder) unmarshalString(value *unstable.Node, v reflect.Value) error {
+ switch v.Kind() {
+ case reflect.String:
+ v.SetString(string(value.Data))
+ case reflect.Interface:
+ v.Set(reflect.ValueOf(string(value.Data)))
+ default:
+ return unstable.NewParserError(d.p.Raw(value.Raw), d.typeMismatchString("string", v.Type()))
+ }
+
+ return nil
+}
+
+func (d *decoder) handleKeyValue(expr *unstable.Node, v reflect.Value) (reflect.Value, error) {
+ d.strict.EnterKeyValue(expr)
+
+ v, err := d.handleKeyValueInner(expr.Key(), expr.Value(), v)
+ if d.skipUntilTable {
+ d.strict.MissingField(expr)
+ d.skipUntilTable = false
+ }
+
+ d.strict.ExitKeyValue(expr)
+
+ return v, err
+}
+
+func (d *decoder) handleKeyValueInner(key unstable.Iterator, value *unstable.Node, v reflect.Value) (reflect.Value, error) {
+ if key.Next() {
+ // Still scoping the key
+ return d.handleKeyValuePart(key, value, v)
+ }
+ // Done scoping the key.
+ // v is whatever Go value we need to fill.
+ return reflect.Value{}, d.handleValue(value, v)
+}
+
+func (d *decoder) keyFromData(keyType reflect.Type, data []byte) (reflect.Value, error) {
+ switch {
+ case stringType.AssignableTo(keyType):
+ return reflect.ValueOf(string(data)), nil
+
+ case stringType.ConvertibleTo(keyType):
+ return reflect.ValueOf(string(data)).Convert(keyType), nil
+
+ case keyType.Implements(textUnmarshalerType):
+ mk := reflect.New(keyType.Elem())
+ if err := mk.Interface().(encoding.TextUnmarshaler).UnmarshalText(data); err != nil {
+ return reflect.Value{}, fmt.Errorf("toml: error unmarshalling key type %s from text: %w", stringType, err)
+ }
+ return mk, nil
+
+ case reflect.PtrTo(keyType).Implements(textUnmarshalerType):
+ mk := reflect.New(keyType)
+ if err := mk.Interface().(encoding.TextUnmarshaler).UnmarshalText(data); err != nil {
+ return reflect.Value{}, fmt.Errorf("toml: error unmarshalling key type %s from text: %w", stringType, err)
+ }
+ return mk.Elem(), nil
+ }
+ return reflect.Value{}, fmt.Errorf("toml: cannot convert map key of type %s to expected type %s", stringType, keyType)
+}
+
+func (d *decoder) handleKeyValuePart(key unstable.Iterator, value *unstable.Node, v reflect.Value) (reflect.Value, error) {
+ // contains the replacement for v
+ var rv reflect.Value
+
+ // First, dispatch over v to make sure it is a valid object.
+ // There is no guarantee over what it could be.
+ switch v.Kind() {
+ case reflect.Map:
+ vt := v.Type()
+
+ mk, err := d.keyFromData(vt.Key(), key.Node().Data)
+ if err != nil {
+ return reflect.Value{}, err
+ }
+
+ // If the map does not exist, create it.
+ if v.IsNil() {
+ v = reflect.MakeMap(vt)
+ rv = v
+ }
+
+ mv := v.MapIndex(mk)
+ set := false
+ if !mv.IsValid() || key.IsLast() {
+ set = true
+ mv = reflect.New(v.Type().Elem()).Elem()
+ }
+
+ nv, err := d.handleKeyValueInner(key, value, mv)
+ if err != nil {
+ return reflect.Value{}, err
+ }
+ if nv.IsValid() {
+ mv = nv
+ set = true
+ }
+
+ if set {
+ v.SetMapIndex(mk, mv)
+ }
+ case reflect.Struct:
+ path, found := structFieldPath(v, string(key.Node().Data))
+ if !found {
+ d.skipUntilTable = true
+ break
+ }
+
+ if d.errorContext == nil {
+ d.errorContext = new(errorContext)
+ }
+ t := v.Type()
+ d.errorContext.Struct = t
+ d.errorContext.Field = path
+
+ f := fieldByIndex(v, path)
+
+ if !f.CanAddr() {
+ // If the field is not addressable, need to take a slower path and
+ // make a copy of the struct itself to a new location.
+ nvp := reflect.New(v.Type())
+ nvp.Elem().Set(v)
+ v = nvp.Elem()
+ _, err := d.handleKeyValuePart(key, value, v)
+ if err != nil {
+ return reflect.Value{}, err
+ }
+ return nvp.Elem(), nil
+ }
+ x, err := d.handleKeyValueInner(key, value, f)
+ if err != nil {
+ return reflect.Value{}, err
+ }
+
+ if x.IsValid() {
+ f.Set(x)
+ }
+ d.errorContext.Struct = nil
+ d.errorContext.Field = nil
+ case reflect.Interface:
+ v = v.Elem()
+
+ // Following encoding/json: decoding an object into an
+ // interface{}, it needs to always hold a
+ // map[string]interface{}. This is for the types to be
+ // consistent whether a previous value was set or not.
+ if !v.IsValid() || v.Type() != mapStringInterfaceType {
+ v = makeMapStringInterface()
+ }
+
+ x, err := d.handleKeyValuePart(key, value, v)
+ if err != nil {
+ return reflect.Value{}, err
+ }
+ if x.IsValid() {
+ v = x
+ }
+ rv = v
+ case reflect.Ptr:
+ elem := v.Elem()
+ if !elem.IsValid() {
+ ptr := reflect.New(v.Type().Elem())
+ v.Set(ptr)
+ rv = v
+ elem = ptr.Elem()
+ }
+
+ elem2, err := d.handleKeyValuePart(key, value, elem)
+ if err != nil {
+ return reflect.Value{}, err
+ }
+ if elem2.IsValid() {
+ elem = elem2
+ }
+ v.Elem().Set(elem)
+ default:
+ return reflect.Value{}, fmt.Errorf("unhandled kv part: %s", v.Kind())
+ }
+
+ return rv, nil
+}
+
+func initAndDereferencePointer(v reflect.Value) reflect.Value {
+ var elem reflect.Value
+ if v.IsNil() {
+ ptr := reflect.New(v.Type().Elem())
+ v.Set(ptr)
+ }
+ elem = v.Elem()
+ return elem
+}
+
+// Same as reflect.Value.FieldByIndex, but creates pointers if needed.
+func fieldByIndex(v reflect.Value, path []int) reflect.Value {
+ for _, x := range path {
+ v = v.Field(x)
+
+ if v.Kind() == reflect.Ptr {
+ if v.IsNil() {
+ v.Set(reflect.New(v.Type().Elem()))
+ }
+ v = v.Elem()
+ }
+ }
+ return v
+}
+
+type fieldPathsMap = map[string][]int
+
+var globalFieldPathsCache atomic.Value // map[danger.TypeID]fieldPathsMap
+
+func structFieldPath(v reflect.Value, name string) ([]int, bool) {
+ t := v.Type()
+
+ cache, _ := globalFieldPathsCache.Load().(map[danger.TypeID]fieldPathsMap)
+ fieldPaths, ok := cache[danger.MakeTypeID(t)]
+
+ if !ok {
+ fieldPaths = map[string][]int{}
+
+ forEachField(t, nil, func(name string, path []int) {
+ fieldPaths[name] = path
+ // extra copy for the case-insensitive match
+ fieldPaths[strings.ToLower(name)] = path
+ })
+
+ newCache := make(map[danger.TypeID]fieldPathsMap, len(cache)+1)
+ newCache[danger.MakeTypeID(t)] = fieldPaths
+ for k, v := range cache {
+ newCache[k] = v
+ }
+ globalFieldPathsCache.Store(newCache)
+ }
+
+ path, ok := fieldPaths[name]
+ if !ok {
+ path, ok = fieldPaths[strings.ToLower(name)]
+ }
+ return path, ok
+}
+
+func forEachField(t reflect.Type, path []int, do func(name string, path []int)) {
+ n := t.NumField()
+ for i := 0; i < n; i++ {
+ f := t.Field(i)
+
+ if !f.Anonymous && f.PkgPath != "" {
+ // only consider exported fields.
+ continue
+ }
+
+ fieldPath := append(path, i)
+ fieldPath = fieldPath[:len(fieldPath):len(fieldPath)]
+
+ name := f.Tag.Get("toml")
+ if name == "-" {
+ continue
+ }
+
+ if i := strings.IndexByte(name, ','); i >= 0 {
+ name = name[:i]
+ }
+
+ if f.Anonymous && name == "" {
+ t2 := f.Type
+ if t2.Kind() == reflect.Ptr {
+ t2 = t2.Elem()
+ }
+
+ if t2.Kind() == reflect.Struct {
+ forEachField(t2, fieldPath, do)
+ }
+ continue
+ }
+
+ if name == "" {
+ name = f.Name
+ }
+
+ do(name, fieldPath)
+ }
+}
diff --git a/vendor/github.com/pelletier/go-toml/v2/unstable/ast.go b/vendor/github.com/pelletier/go-toml/v2/unstable/ast.go
new file mode 100644
index 00000000..f526bf2c
--- /dev/null
+++ b/vendor/github.com/pelletier/go-toml/v2/unstable/ast.go
@@ -0,0 +1,136 @@
+package unstable
+
+import (
+ "fmt"
+ "unsafe"
+
+ "github.com/pelletier/go-toml/v2/internal/danger"
+)
+
+// Iterator over a sequence of nodes.
+//
+// Starts uninitialized, you need to call Next() first.
+//
+// For example:
+//
+// it := n.Children()
+// for it.Next() {
+// n := it.Node()
+// // do something with n
+// }
+type Iterator struct {
+ started bool
+ node *Node
+}
+
+// Next moves the iterator forward and returns true if points to a
+// node, false otherwise.
+func (c *Iterator) Next() bool {
+ if !c.started {
+ c.started = true
+ } else if c.node.Valid() {
+ c.node = c.node.Next()
+ }
+ return c.node.Valid()
+}
+
+// IsLast returns true if the current node of the iterator is the last
+// one. Subsequent calls to Next() will return false.
+func (c *Iterator) IsLast() bool {
+ return c.node.next == 0
+}
+
+// Node returns a pointer to the node pointed at by the iterator.
+func (c *Iterator) Node() *Node {
+ return c.node
+}
+
+// Node in a TOML expression AST.
+//
+// Depending on Kind, its sequence of children should be interpreted
+// differently.
+//
+// - Array have one child per element in the array.
+// - InlineTable have one child per key-value in the table (each of kind
+// InlineTable).
+// - KeyValue have at least two children. The first one is the value. The rest
+// make a potentially dotted key.
+// - Table and ArrayTable's children represent a dotted key (same as
+// KeyValue, but without the first node being the value).
+//
+// When relevant, Raw describes the range of bytes this node is referring to in
+// the input document. Use Parser.Raw() to retrieve the actual bytes.
+type Node struct {
+ Kind Kind
+ Raw Range // Raw bytes from the input.
+ Data []byte // Node value (either allocated or referencing the input).
+
+ // References to other nodes, as offsets in the backing array
+ // from this node. References can go backward, so those can be
+ // negative.
+ next int // 0 if last element
+ child int // 0 if no child
+}
+
+// Range of bytes in the document.
+type Range struct {
+ Offset uint32
+ Length uint32
+}
+
+// Next returns a pointer to the next node, or nil if there is no next node.
+func (n *Node) Next() *Node {
+ if n.next == 0 {
+ return nil
+ }
+ ptr := unsafe.Pointer(n)
+ size := unsafe.Sizeof(Node{})
+ return (*Node)(danger.Stride(ptr, size, n.next))
+}
+
+// Child returns a pointer to the first child node of this node. Other children
+// can be accessed calling Next on the first child. Returns an nil if this Node
+// has no child.
+func (n *Node) Child() *Node {
+ if n.child == 0 {
+ return nil
+ }
+ ptr := unsafe.Pointer(n)
+ size := unsafe.Sizeof(Node{})
+ return (*Node)(danger.Stride(ptr, size, n.child))
+}
+
+// Valid returns true if the node's kind is set (not to Invalid).
+func (n *Node) Valid() bool {
+ return n != nil
+}
+
+// Key returns the children nodes making the Key on a supported node. Panics
+// otherwise. They are guaranteed to be all be of the Kind Key. A simple key
+// would return just one element.
+func (n *Node) Key() Iterator {
+ switch n.Kind {
+ case KeyValue:
+ value := n.Child()
+ if !value.Valid() {
+ panic(fmt.Errorf("KeyValue should have at least two children"))
+ }
+ return Iterator{node: value.Next()}
+ case Table, ArrayTable:
+ return Iterator{node: n.Child()}
+ default:
+ panic(fmt.Errorf("Key() is not supported on a %s", n.Kind))
+ }
+}
+
+// Value returns a pointer to the value node of a KeyValue.
+// Guaranteed to be non-nil. Panics if not called on a KeyValue node,
+// or if the Children are malformed.
+func (n *Node) Value() *Node {
+ return n.Child()
+}
+
+// Children returns an iterator over a node's children.
+func (n *Node) Children() Iterator {
+ return Iterator{node: n.Child()}
+}
diff --git a/vendor/github.com/pelletier/go-toml/v2/unstable/builder.go b/vendor/github.com/pelletier/go-toml/v2/unstable/builder.go
new file mode 100644
index 00000000..9538e30d
--- /dev/null
+++ b/vendor/github.com/pelletier/go-toml/v2/unstable/builder.go
@@ -0,0 +1,71 @@
+package unstable
+
+// root contains a full AST.
+//
+// It is immutable once constructed with Builder.
+type root struct {
+ nodes []Node
+}
+
+// Iterator over the top level nodes.
+func (r *root) Iterator() Iterator {
+ it := Iterator{}
+ if len(r.nodes) > 0 {
+ it.node = &r.nodes[0]
+ }
+ return it
+}
+
+func (r *root) at(idx reference) *Node {
+ return &r.nodes[idx]
+}
+
+type reference int
+
+const invalidReference reference = -1
+
+func (r reference) Valid() bool {
+ return r != invalidReference
+}
+
+type builder struct {
+ tree root
+ lastIdx int
+}
+
+func (b *builder) Tree() *root {
+ return &b.tree
+}
+
+func (b *builder) NodeAt(ref reference) *Node {
+ return b.tree.at(ref)
+}
+
+func (b *builder) Reset() {
+ b.tree.nodes = b.tree.nodes[:0]
+ b.lastIdx = 0
+}
+
+func (b *builder) Push(n Node) reference {
+ b.lastIdx = len(b.tree.nodes)
+ b.tree.nodes = append(b.tree.nodes, n)
+ return reference(b.lastIdx)
+}
+
+func (b *builder) PushAndChain(n Node) reference {
+ newIdx := len(b.tree.nodes)
+ b.tree.nodes = append(b.tree.nodes, n)
+ if b.lastIdx >= 0 {
+ b.tree.nodes[b.lastIdx].next = newIdx - b.lastIdx
+ }
+ b.lastIdx = newIdx
+ return reference(b.lastIdx)
+}
+
+func (b *builder) AttachChild(parent reference, child reference) {
+ b.tree.nodes[parent].child = int(child) - int(parent)
+}
+
+func (b *builder) Chain(from reference, to reference) {
+ b.tree.nodes[from].next = int(to) - int(from)
+}
diff --git a/vendor/github.com/pelletier/go-toml/v2/unstable/doc.go b/vendor/github.com/pelletier/go-toml/v2/unstable/doc.go
new file mode 100644
index 00000000..7ff26c53
--- /dev/null
+++ b/vendor/github.com/pelletier/go-toml/v2/unstable/doc.go
@@ -0,0 +1,3 @@
+// Package unstable provides APIs that do not meet the backward compatibility
+// guarantees yet.
+package unstable
diff --git a/vendor/github.com/pelletier/go-toml/v2/unstable/kind.go b/vendor/github.com/pelletier/go-toml/v2/unstable/kind.go
new file mode 100644
index 00000000..ff9df1be
--- /dev/null
+++ b/vendor/github.com/pelletier/go-toml/v2/unstable/kind.go
@@ -0,0 +1,71 @@
+package unstable
+
+import "fmt"
+
+// Kind represents the type of TOML structure contained in a given Node.
+type Kind int
+
+const (
+ // Meta
+ Invalid Kind = iota
+ Comment
+ Key
+
+ // Top level structures
+ Table
+ ArrayTable
+ KeyValue
+
+ // Containers values
+ Array
+ InlineTable
+
+ // Values
+ String
+ Bool
+ Float
+ Integer
+ LocalDate
+ LocalTime
+ LocalDateTime
+ DateTime
+)
+
+// String implementation of fmt.Stringer.
+func (k Kind) String() string {
+ switch k {
+ case Invalid:
+ return "Invalid"
+ case Comment:
+ return "Comment"
+ case Key:
+ return "Key"
+ case Table:
+ return "Table"
+ case ArrayTable:
+ return "ArrayTable"
+ case KeyValue:
+ return "KeyValue"
+ case Array:
+ return "Array"
+ case InlineTable:
+ return "InlineTable"
+ case String:
+ return "String"
+ case Bool:
+ return "Bool"
+ case Float:
+ return "Float"
+ case Integer:
+ return "Integer"
+ case LocalDate:
+ return "LocalDate"
+ case LocalTime:
+ return "LocalTime"
+ case LocalDateTime:
+ return "LocalDateTime"
+ case DateTime:
+ return "DateTime"
+ }
+ panic(fmt.Errorf("Kind.String() not implemented for '%d'", k))
+}
diff --git a/vendor/github.com/pelletier/go-toml/v2/unstable/parser.go b/vendor/github.com/pelletier/go-toml/v2/unstable/parser.go
new file mode 100644
index 00000000..50358a44
--- /dev/null
+++ b/vendor/github.com/pelletier/go-toml/v2/unstable/parser.go
@@ -0,0 +1,1245 @@
+package unstable
+
+import (
+ "bytes"
+ "fmt"
+ "unicode"
+
+ "github.com/pelletier/go-toml/v2/internal/characters"
+ "github.com/pelletier/go-toml/v2/internal/danger"
+)
+
+// ParserError describes an error relative to the content of the document.
+//
+// It cannot outlive the instance of Parser it refers to, and may cause panics
+// if the parser is reset.
+type ParserError struct {
+ Highlight []byte
+ Message string
+ Key []string // optional
+}
+
+// Error is the implementation of the error interface.
+func (e *ParserError) Error() string {
+ return e.Message
+}
+
+// NewParserError is a convenience function to create a ParserError
+//
+// Warning: Highlight needs to be a subslice of Parser.data, so only slices
+// returned by Parser.Raw are valid candidates.
+func NewParserError(highlight []byte, format string, args ...interface{}) error {
+ return &ParserError{
+ Highlight: highlight,
+ Message: fmt.Errorf(format, args...).Error(),
+ }
+}
+
+// Parser scans over a TOML-encoded document and generates an iterative AST.
+//
+// To prime the Parser, first reset it with the contents of a TOML document.
+// Then, process all top-level expressions sequentially. See Example.
+//
+// Don't forget to check Error() after you're done parsing.
+//
+// Each top-level expression needs to be fully processed before calling
+// NextExpression() again. Otherwise, calls to various Node methods may panic if
+// the parser has moved on the next expression.
+//
+// For performance reasons, go-toml doesn't make a copy of the input bytes to
+// the parser. Make sure to copy all the bytes you need to outlive the slice
+// given to the parser.
+type Parser struct {
+ data []byte
+ builder builder
+ ref reference
+ left []byte
+ err error
+ first bool
+
+ KeepComments bool
+}
+
+// Data returns the slice provided to the last call to Reset.
+func (p *Parser) Data() []byte {
+ return p.data
+}
+
+// Range returns a range description that corresponds to a given slice of the
+// input. If the argument is not a subslice of the parser input, this function
+// panics.
+func (p *Parser) Range(b []byte) Range {
+ return Range{
+ Offset: uint32(danger.SubsliceOffset(p.data, b)),
+ Length: uint32(len(b)),
+ }
+}
+
+// Raw returns the slice corresponding to the bytes in the given range.
+func (p *Parser) Raw(raw Range) []byte {
+ return p.data[raw.Offset : raw.Offset+raw.Length]
+}
+
+// Reset brings the parser to its initial state for a given input. It wipes an
+// reuses internal storage to reduce allocation.
+func (p *Parser) Reset(b []byte) {
+ p.builder.Reset()
+ p.ref = invalidReference
+ p.data = b
+ p.left = b
+ p.err = nil
+ p.first = true
+}
+
+// NextExpression parses the next top-level expression. If an expression was
+// successfully parsed, it returns true. If the parser is at the end of the
+// document or an error occurred, it returns false.
+//
+// Retrieve the parsed expression with Expression().
+func (p *Parser) NextExpression() bool {
+ if len(p.left) == 0 || p.err != nil {
+ return false
+ }
+
+ p.builder.Reset()
+ p.ref = invalidReference
+
+ for {
+ if len(p.left) == 0 || p.err != nil {
+ return false
+ }
+
+ if !p.first {
+ p.left, p.err = p.parseNewline(p.left)
+ }
+
+ if len(p.left) == 0 || p.err != nil {
+ return false
+ }
+
+ p.ref, p.left, p.err = p.parseExpression(p.left)
+
+ if p.err != nil {
+ return false
+ }
+
+ p.first = false
+
+ if p.ref.Valid() {
+ return true
+ }
+ }
+}
+
+// Expression returns a pointer to the node representing the last successfully
+// parsed expression.
+func (p *Parser) Expression() *Node {
+ return p.builder.NodeAt(p.ref)
+}
+
+// Error returns any error that has occurred during parsing.
+func (p *Parser) Error() error {
+ return p.err
+}
+
+// Position describes a position in the input.
+type Position struct {
+ // Number of bytes from the beginning of the input.
+ Offset int
+ // Line number, starting at 1.
+ Line int
+ // Column number, starting at 1.
+ Column int
+}
+
+// Shape describes the position of a range in the input.
+type Shape struct {
+ Start Position
+ End Position
+}
+
+func (p *Parser) position(b []byte) Position {
+ offset := danger.SubsliceOffset(p.data, b)
+
+ lead := p.data[:offset]
+
+ return Position{
+ Offset: offset,
+ Line: bytes.Count(lead, []byte{'\n'}) + 1,
+ Column: len(lead) - bytes.LastIndex(lead, []byte{'\n'}),
+ }
+}
+
+// Shape returns the shape of the given range in the input. Will
+// panic if the range is not a subslice of the input.
+func (p *Parser) Shape(r Range) Shape {
+ raw := p.Raw(r)
+ return Shape{
+ Start: p.position(raw),
+ End: p.position(raw[r.Length:]),
+ }
+}
+
+func (p *Parser) parseNewline(b []byte) ([]byte, error) {
+ if b[0] == '\n' {
+ return b[1:], nil
+ }
+
+ if b[0] == '\r' {
+ _, rest, err := scanWindowsNewline(b)
+ return rest, err
+ }
+
+ return nil, NewParserError(b[0:1], "expected newline but got %#U", b[0])
+}
+
+func (p *Parser) parseComment(b []byte) (reference, []byte, error) {
+ ref := invalidReference
+ data, rest, err := scanComment(b)
+ if p.KeepComments && err == nil {
+ ref = p.builder.Push(Node{
+ Kind: Comment,
+ Raw: p.Range(data),
+ Data: data,
+ })
+ }
+ return ref, rest, err
+}
+
+func (p *Parser) parseExpression(b []byte) (reference, []byte, error) {
+ // expression = ws [ comment ]
+ // expression =/ ws keyval ws [ comment ]
+ // expression =/ ws table ws [ comment ]
+ ref := invalidReference
+
+ b = p.parseWhitespace(b)
+
+ if len(b) == 0 {
+ return ref, b, nil
+ }
+
+ if b[0] == '#' {
+ ref, rest, err := p.parseComment(b)
+ return ref, rest, err
+ }
+
+ if b[0] == '\n' || b[0] == '\r' {
+ return ref, b, nil
+ }
+
+ var err error
+ if b[0] == '[' {
+ ref, b, err = p.parseTable(b)
+ } else {
+ ref, b, err = p.parseKeyval(b)
+ }
+
+ if err != nil {
+ return ref, nil, err
+ }
+
+ b = p.parseWhitespace(b)
+
+ if len(b) > 0 && b[0] == '#' {
+ cref, rest, err := p.parseComment(b)
+ if cref != invalidReference {
+ p.builder.Chain(ref, cref)
+ }
+ return ref, rest, err
+ }
+
+ return ref, b, nil
+}
+
+func (p *Parser) parseTable(b []byte) (reference, []byte, error) {
+ // table = std-table / array-table
+ if len(b) > 1 && b[1] == '[' {
+ return p.parseArrayTable(b)
+ }
+
+ return p.parseStdTable(b)
+}
+
+func (p *Parser) parseArrayTable(b []byte) (reference, []byte, error) {
+ // array-table = array-table-open key array-table-close
+ // array-table-open = %x5B.5B ws ; [[ Double left square bracket
+ // array-table-close = ws %x5D.5D ; ]] Double right square bracket
+ ref := p.builder.Push(Node{
+ Kind: ArrayTable,
+ })
+
+ b = b[2:]
+ b = p.parseWhitespace(b)
+
+ k, b, err := p.parseKey(b)
+ if err != nil {
+ return ref, nil, err
+ }
+
+ p.builder.AttachChild(ref, k)
+ b = p.parseWhitespace(b)
+
+ b, err = expect(']', b)
+ if err != nil {
+ return ref, nil, err
+ }
+
+ b, err = expect(']', b)
+
+ return ref, b, err
+}
+
+func (p *Parser) parseStdTable(b []byte) (reference, []byte, error) {
+ // std-table = std-table-open key std-table-close
+ // std-table-open = %x5B ws ; [ Left square bracket
+ // std-table-close = ws %x5D ; ] Right square bracket
+ ref := p.builder.Push(Node{
+ Kind: Table,
+ })
+
+ b = b[1:]
+ b = p.parseWhitespace(b)
+
+ key, b, err := p.parseKey(b)
+ if err != nil {
+ return ref, nil, err
+ }
+
+ p.builder.AttachChild(ref, key)
+
+ b = p.parseWhitespace(b)
+
+ b, err = expect(']', b)
+
+ return ref, b, err
+}
+
+func (p *Parser) parseKeyval(b []byte) (reference, []byte, error) {
+ // keyval = key keyval-sep val
+ ref := p.builder.Push(Node{
+ Kind: KeyValue,
+ })
+
+ key, b, err := p.parseKey(b)
+ if err != nil {
+ return invalidReference, nil, err
+ }
+
+ // keyval-sep = ws %x3D ws ; =
+
+ b = p.parseWhitespace(b)
+
+ if len(b) == 0 {
+ return invalidReference, nil, NewParserError(b, "expected = after a key, but the document ends there")
+ }
+
+ b, err = expect('=', b)
+ if err != nil {
+ return invalidReference, nil, err
+ }
+
+ b = p.parseWhitespace(b)
+
+ valRef, b, err := p.parseVal(b)
+ if err != nil {
+ return ref, b, err
+ }
+
+ p.builder.Chain(valRef, key)
+ p.builder.AttachChild(ref, valRef)
+
+ return ref, b, err
+}
+
+//nolint:cyclop,funlen
+func (p *Parser) parseVal(b []byte) (reference, []byte, error) {
+ // val = string / boolean / array / inline-table / date-time / float / integer
+ ref := invalidReference
+
+ if len(b) == 0 {
+ return ref, nil, NewParserError(b, "expected value, not eof")
+ }
+
+ var err error
+ c := b[0]
+
+ switch c {
+ case '"':
+ var raw []byte
+ var v []byte
+ if scanFollowsMultilineBasicStringDelimiter(b) {
+ raw, v, b, err = p.parseMultilineBasicString(b)
+ } else {
+ raw, v, b, err = p.parseBasicString(b)
+ }
+
+ if err == nil {
+ ref = p.builder.Push(Node{
+ Kind: String,
+ Raw: p.Range(raw),
+ Data: v,
+ })
+ }
+
+ return ref, b, err
+ case '\'':
+ var raw []byte
+ var v []byte
+ if scanFollowsMultilineLiteralStringDelimiter(b) {
+ raw, v, b, err = p.parseMultilineLiteralString(b)
+ } else {
+ raw, v, b, err = p.parseLiteralString(b)
+ }
+
+ if err == nil {
+ ref = p.builder.Push(Node{
+ Kind: String,
+ Raw: p.Range(raw),
+ Data: v,
+ })
+ }
+
+ return ref, b, err
+ case 't':
+ if !scanFollowsTrue(b) {
+ return ref, nil, NewParserError(atmost(b, 4), "expected 'true'")
+ }
+
+ ref = p.builder.Push(Node{
+ Kind: Bool,
+ Data: b[:4],
+ })
+
+ return ref, b[4:], nil
+ case 'f':
+ if !scanFollowsFalse(b) {
+ return ref, nil, NewParserError(atmost(b, 5), "expected 'false'")
+ }
+
+ ref = p.builder.Push(Node{
+ Kind: Bool,
+ Data: b[:5],
+ })
+
+ return ref, b[5:], nil
+ case '[':
+ return p.parseValArray(b)
+ case '{':
+ return p.parseInlineTable(b)
+ default:
+ return p.parseIntOrFloatOrDateTime(b)
+ }
+}
+
+func atmost(b []byte, n int) []byte {
+ if n >= len(b) {
+ return b
+ }
+
+ return b[:n]
+}
+
+func (p *Parser) parseLiteralString(b []byte) ([]byte, []byte, []byte, error) {
+ v, rest, err := scanLiteralString(b)
+ if err != nil {
+ return nil, nil, nil, err
+ }
+
+ return v, v[1 : len(v)-1], rest, nil
+}
+
+func (p *Parser) parseInlineTable(b []byte) (reference, []byte, error) {
+ // inline-table = inline-table-open [ inline-table-keyvals ] inline-table-close
+ // inline-table-open = %x7B ws ; {
+ // inline-table-close = ws %x7D ; }
+ // inline-table-sep = ws %x2C ws ; , Comma
+ // inline-table-keyvals = keyval [ inline-table-sep inline-table-keyvals ]
+ parent := p.builder.Push(Node{
+ Kind: InlineTable,
+ Raw: p.Range(b[:1]),
+ })
+
+ first := true
+
+ var child reference
+
+ b = b[1:]
+
+ var err error
+
+ for len(b) > 0 {
+ previousB := b
+ b = p.parseWhitespace(b)
+
+ if len(b) == 0 {
+ return parent, nil, NewParserError(previousB[:1], "inline table is incomplete")
+ }
+
+ if b[0] == '}' {
+ break
+ }
+
+ if !first {
+ b, err = expect(',', b)
+ if err != nil {
+ return parent, nil, err
+ }
+ b = p.parseWhitespace(b)
+ }
+
+ var kv reference
+
+ kv, b, err = p.parseKeyval(b)
+ if err != nil {
+ return parent, nil, err
+ }
+
+ if first {
+ p.builder.AttachChild(parent, kv)
+ } else {
+ p.builder.Chain(child, kv)
+ }
+ child = kv
+
+ first = false
+ }
+
+ rest, err := expect('}', b)
+
+ return parent, rest, err
+}
+
+//nolint:funlen,cyclop
+func (p *Parser) parseValArray(b []byte) (reference, []byte, error) {
+ // array = array-open [ array-values ] ws-comment-newline array-close
+ // array-open = %x5B ; [
+ // array-close = %x5D ; ]
+ // array-values = ws-comment-newline val ws-comment-newline array-sep array-values
+ // array-values =/ ws-comment-newline val ws-comment-newline [ array-sep ]
+ // array-sep = %x2C ; , Comma
+ // ws-comment-newline = *( wschar / [ comment ] newline )
+ arrayStart := b
+ b = b[1:]
+
+ parent := p.builder.Push(Node{
+ Kind: Array,
+ })
+
+ // First indicates whether the parser is looking for the first element
+ // (non-comment) of the array.
+ first := true
+
+ lastChild := invalidReference
+
+ addChild := func(valueRef reference) {
+ if lastChild == invalidReference {
+ p.builder.AttachChild(parent, valueRef)
+ } else {
+ p.builder.Chain(lastChild, valueRef)
+ }
+ lastChild = valueRef
+ }
+
+ var err error
+ for len(b) > 0 {
+ cref := invalidReference
+ cref, b, err = p.parseOptionalWhitespaceCommentNewline(b)
+ if err != nil {
+ return parent, nil, err
+ }
+
+ if cref != invalidReference {
+ addChild(cref)
+ }
+
+ if len(b) == 0 {
+ return parent, nil, NewParserError(arrayStart[:1], "array is incomplete")
+ }
+
+ if b[0] == ']' {
+ break
+ }
+
+ if b[0] == ',' {
+ if first {
+ return parent, nil, NewParserError(b[0:1], "array cannot start with comma")
+ }
+ b = b[1:]
+
+ cref, b, err = p.parseOptionalWhitespaceCommentNewline(b)
+ if err != nil {
+ return parent, nil, err
+ }
+ if cref != invalidReference {
+ addChild(cref)
+ }
+ } else if !first {
+ return parent, nil, NewParserError(b[0:1], "array elements must be separated by commas")
+ }
+
+ // TOML allows trailing commas in arrays.
+ if len(b) > 0 && b[0] == ']' {
+ break
+ }
+
+ var valueRef reference
+ valueRef, b, err = p.parseVal(b)
+ if err != nil {
+ return parent, nil, err
+ }
+
+ addChild(valueRef)
+
+ cref, b, err = p.parseOptionalWhitespaceCommentNewline(b)
+ if err != nil {
+ return parent, nil, err
+ }
+ if cref != invalidReference {
+ addChild(cref)
+ }
+
+ first = false
+ }
+
+ rest, err := expect(']', b)
+
+ return parent, rest, err
+}
+
+func (p *Parser) parseOptionalWhitespaceCommentNewline(b []byte) (reference, []byte, error) {
+ rootCommentRef := invalidReference
+ latestCommentRef := invalidReference
+
+ addComment := func(ref reference) {
+ if rootCommentRef == invalidReference {
+ rootCommentRef = ref
+ } else if latestCommentRef == invalidReference {
+ p.builder.AttachChild(rootCommentRef, ref)
+ latestCommentRef = ref
+ } else {
+ p.builder.Chain(latestCommentRef, ref)
+ latestCommentRef = ref
+ }
+ }
+
+ for len(b) > 0 {
+ var err error
+ b = p.parseWhitespace(b)
+
+ if len(b) > 0 && b[0] == '#' {
+ var ref reference
+ ref, b, err = p.parseComment(b)
+ if err != nil {
+ return invalidReference, nil, err
+ }
+ if ref != invalidReference {
+ addComment(ref)
+ }
+ }
+
+ if len(b) == 0 {
+ break
+ }
+
+ if b[0] == '\n' || b[0] == '\r' {
+ b, err = p.parseNewline(b)
+ if err != nil {
+ return invalidReference, nil, err
+ }
+ } else {
+ break
+ }
+ }
+
+ return rootCommentRef, b, nil
+}
+
+func (p *Parser) parseMultilineLiteralString(b []byte) ([]byte, []byte, []byte, error) {
+ token, rest, err := scanMultilineLiteralString(b)
+ if err != nil {
+ return nil, nil, nil, err
+ }
+
+ i := 3
+
+ // skip the immediate new line
+ if token[i] == '\n' {
+ i++
+ } else if token[i] == '\r' && token[i+1] == '\n' {
+ i += 2
+ }
+
+ return token, token[i : len(token)-3], rest, err
+}
+
+//nolint:funlen,gocognit,cyclop
+func (p *Parser) parseMultilineBasicString(b []byte) ([]byte, []byte, []byte, error) {
+ // ml-basic-string = ml-basic-string-delim [ newline ] ml-basic-body
+ // ml-basic-string-delim
+ // ml-basic-string-delim = 3quotation-mark
+ // ml-basic-body = *mlb-content *( mlb-quotes 1*mlb-content ) [ mlb-quotes ]
+ //
+ // mlb-content = mlb-char / newline / mlb-escaped-nl
+ // mlb-char = mlb-unescaped / escaped
+ // mlb-quotes = 1*2quotation-mark
+ // mlb-unescaped = wschar / %x21 / %x23-5B / %x5D-7E / non-ascii
+ // mlb-escaped-nl = escape ws newline *( wschar / newline )
+ token, escaped, rest, err := scanMultilineBasicString(b)
+ if err != nil {
+ return nil, nil, nil, err
+ }
+
+ i := 3
+
+ // skip the immediate new line
+ if token[i] == '\n' {
+ i++
+ } else if token[i] == '\r' && token[i+1] == '\n' {
+ i += 2
+ }
+
+ // fast path
+ startIdx := i
+ endIdx := len(token) - len(`"""`)
+
+ if !escaped {
+ str := token[startIdx:endIdx]
+ verr := characters.Utf8TomlValidAlreadyEscaped(str)
+ if verr.Zero() {
+ return token, str, rest, nil
+ }
+ return nil, nil, nil, NewParserError(str[verr.Index:verr.Index+verr.Size], "invalid UTF-8")
+ }
+
+ var builder bytes.Buffer
+
+ // The scanner ensures that the token starts and ends with quotes and that
+ // escapes are balanced.
+ for i < len(token)-3 {
+ c := token[i]
+
+ //nolint:nestif
+ if c == '\\' {
+ // When the last non-whitespace character on a line is an unescaped \,
+ // it will be trimmed along with all whitespace (including newlines) up
+ // to the next non-whitespace character or closing delimiter.
+
+ isLastNonWhitespaceOnLine := false
+ j := 1
+ findEOLLoop:
+ for ; j < len(token)-3-i; j++ {
+ switch token[i+j] {
+ case ' ', '\t':
+ continue
+ case '\r':
+ if token[i+j+1] == '\n' {
+ continue
+ }
+ case '\n':
+ isLastNonWhitespaceOnLine = true
+ }
+ break findEOLLoop
+ }
+ if isLastNonWhitespaceOnLine {
+ i += j
+ for ; i < len(token)-3; i++ {
+ c := token[i]
+ if !(c == '\n' || c == '\r' || c == ' ' || c == '\t') {
+ i--
+ break
+ }
+ }
+ i++
+ continue
+ }
+
+ // handle escaping
+ i++
+ c = token[i]
+
+ switch c {
+ case '"', '\\':
+ builder.WriteByte(c)
+ case 'b':
+ builder.WriteByte('\b')
+ case 'f':
+ builder.WriteByte('\f')
+ case 'n':
+ builder.WriteByte('\n')
+ case 'r':
+ builder.WriteByte('\r')
+ case 't':
+ builder.WriteByte('\t')
+ case 'e':
+ builder.WriteByte(0x1B)
+ case 'u':
+ x, err := hexToRune(atmost(token[i+1:], 4), 4)
+ if err != nil {
+ return nil, nil, nil, err
+ }
+ builder.WriteRune(x)
+ i += 4
+ case 'U':
+ x, err := hexToRune(atmost(token[i+1:], 8), 8)
+ if err != nil {
+ return nil, nil, nil, err
+ }
+
+ builder.WriteRune(x)
+ i += 8
+ default:
+ return nil, nil, nil, NewParserError(token[i:i+1], "invalid escaped character %#U", c)
+ }
+ i++
+ } else {
+ size := characters.Utf8ValidNext(token[i:])
+ if size == 0 {
+ return nil, nil, nil, NewParserError(token[i:i+1], "invalid character %#U", c)
+ }
+ builder.Write(token[i : i+size])
+ i += size
+ }
+ }
+
+ return token, builder.Bytes(), rest, nil
+}
+
+func (p *Parser) parseKey(b []byte) (reference, []byte, error) {
+ // key = simple-key / dotted-key
+ // simple-key = quoted-key / unquoted-key
+ //
+ // unquoted-key = 1*( ALPHA / DIGIT / %x2D / %x5F ) ; A-Z / a-z / 0-9 / - / _
+ // quoted-key = basic-string / literal-string
+ // dotted-key = simple-key 1*( dot-sep simple-key )
+ //
+ // dot-sep = ws %x2E ws ; . Period
+ raw, key, b, err := p.parseSimpleKey(b)
+ if err != nil {
+ return invalidReference, nil, err
+ }
+
+ ref := p.builder.Push(Node{
+ Kind: Key,
+ Raw: p.Range(raw),
+ Data: key,
+ })
+
+ for {
+ b = p.parseWhitespace(b)
+ if len(b) > 0 && b[0] == '.' {
+ b = p.parseWhitespace(b[1:])
+
+ raw, key, b, err = p.parseSimpleKey(b)
+ if err != nil {
+ return ref, nil, err
+ }
+
+ p.builder.PushAndChain(Node{
+ Kind: Key,
+ Raw: p.Range(raw),
+ Data: key,
+ })
+ } else {
+ break
+ }
+ }
+
+ return ref, b, nil
+}
+
+func (p *Parser) parseSimpleKey(b []byte) (raw, key, rest []byte, err error) {
+ if len(b) == 0 {
+ return nil, nil, nil, NewParserError(b, "expected key but found none")
+ }
+
+ // simple-key = quoted-key / unquoted-key
+ // unquoted-key = 1*( ALPHA / DIGIT / %x2D / %x5F ) ; A-Z / a-z / 0-9 / - / _
+ // quoted-key = basic-string / literal-string
+ switch {
+ case b[0] == '\'':
+ return p.parseLiteralString(b)
+ case b[0] == '"':
+ return p.parseBasicString(b)
+ case isUnquotedKeyChar(b[0]):
+ key, rest = scanUnquotedKey(b)
+ return key, key, rest, nil
+ default:
+ return nil, nil, nil, NewParserError(b[0:1], "invalid character at start of key: %c", b[0])
+ }
+}
+
+//nolint:funlen,cyclop
+func (p *Parser) parseBasicString(b []byte) ([]byte, []byte, []byte, error) {
+ // basic-string = quotation-mark *basic-char quotation-mark
+ // quotation-mark = %x22 ; "
+ // basic-char = basic-unescaped / escaped
+ // basic-unescaped = wschar / %x21 / %x23-5B / %x5D-7E / non-ascii
+ // escaped = escape escape-seq-char
+ // escape-seq-char = %x22 ; " quotation mark U+0022
+ // escape-seq-char =/ %x5C ; \ reverse solidus U+005C
+ // escape-seq-char =/ %x62 ; b backspace U+0008
+ // escape-seq-char =/ %x66 ; f form feed U+000C
+ // escape-seq-char =/ %x6E ; n line feed U+000A
+ // escape-seq-char =/ %x72 ; r carriage return U+000D
+ // escape-seq-char =/ %x74 ; t tab U+0009
+ // escape-seq-char =/ %x75 4HEXDIG ; uXXXX U+XXXX
+ // escape-seq-char =/ %x55 8HEXDIG ; UXXXXXXXX U+XXXXXXXX
+ token, escaped, rest, err := scanBasicString(b)
+ if err != nil {
+ return nil, nil, nil, err
+ }
+
+ startIdx := len(`"`)
+ endIdx := len(token) - len(`"`)
+
+ // Fast path. If there is no escape sequence, the string should just be
+ // an UTF-8 encoded string, which is the same as Go. In that case,
+ // validate the string and return a direct reference to the buffer.
+ if !escaped {
+ str := token[startIdx:endIdx]
+ verr := characters.Utf8TomlValidAlreadyEscaped(str)
+ if verr.Zero() {
+ return token, str, rest, nil
+ }
+ return nil, nil, nil, NewParserError(str[verr.Index:verr.Index+verr.Size], "invalid UTF-8")
+ }
+
+ i := startIdx
+
+ var builder bytes.Buffer
+
+ // The scanner ensures that the token starts and ends with quotes and that
+ // escapes are balanced.
+ for i < len(token)-1 {
+ c := token[i]
+ if c == '\\' {
+ i++
+ c = token[i]
+
+ switch c {
+ case '"', '\\':
+ builder.WriteByte(c)
+ case 'b':
+ builder.WriteByte('\b')
+ case 'f':
+ builder.WriteByte('\f')
+ case 'n':
+ builder.WriteByte('\n')
+ case 'r':
+ builder.WriteByte('\r')
+ case 't':
+ builder.WriteByte('\t')
+ case 'e':
+ builder.WriteByte(0x1B)
+ case 'u':
+ x, err := hexToRune(token[i+1:len(token)-1], 4)
+ if err != nil {
+ return nil, nil, nil, err
+ }
+
+ builder.WriteRune(x)
+ i += 4
+ case 'U':
+ x, err := hexToRune(token[i+1:len(token)-1], 8)
+ if err != nil {
+ return nil, nil, nil, err
+ }
+
+ builder.WriteRune(x)
+ i += 8
+ default:
+ return nil, nil, nil, NewParserError(token[i:i+1], "invalid escaped character %#U", c)
+ }
+ i++
+ } else {
+ size := characters.Utf8ValidNext(token[i:])
+ if size == 0 {
+ return nil, nil, nil, NewParserError(token[i:i+1], "invalid character %#U", c)
+ }
+ builder.Write(token[i : i+size])
+ i += size
+ }
+ }
+
+ return token, builder.Bytes(), rest, nil
+}
+
+func hexToRune(b []byte, length int) (rune, error) {
+ if len(b) < length {
+ return -1, NewParserError(b, "unicode point needs %d character, not %d", length, len(b))
+ }
+ b = b[:length]
+
+ var r uint32
+ for i, c := range b {
+ d := uint32(0)
+ switch {
+ case '0' <= c && c <= '9':
+ d = uint32(c - '0')
+ case 'a' <= c && c <= 'f':
+ d = uint32(c - 'a' + 10)
+ case 'A' <= c && c <= 'F':
+ d = uint32(c - 'A' + 10)
+ default:
+ return -1, NewParserError(b[i:i+1], "non-hex character")
+ }
+ r = r*16 + d
+ }
+
+ if r > unicode.MaxRune || 0xD800 <= r && r < 0xE000 {
+ return -1, NewParserError(b, "escape sequence is invalid Unicode code point")
+ }
+
+ return rune(r), nil
+}
+
+func (p *Parser) parseWhitespace(b []byte) []byte {
+ // ws = *wschar
+ // wschar = %x20 ; Space
+ // wschar =/ %x09 ; Horizontal tab
+ _, rest := scanWhitespace(b)
+
+ return rest
+}
+
+//nolint:cyclop
+func (p *Parser) parseIntOrFloatOrDateTime(b []byte) (reference, []byte, error) {
+ switch b[0] {
+ case 'i':
+ if !scanFollowsInf(b) {
+ return invalidReference, nil, NewParserError(atmost(b, 3), "expected 'inf'")
+ }
+
+ return p.builder.Push(Node{
+ Kind: Float,
+ Data: b[:3],
+ Raw: p.Range(b[:3]),
+ }), b[3:], nil
+ case 'n':
+ if !scanFollowsNan(b) {
+ return invalidReference, nil, NewParserError(atmost(b, 3), "expected 'nan'")
+ }
+
+ return p.builder.Push(Node{
+ Kind: Float,
+ Data: b[:3],
+ Raw: p.Range(b[:3]),
+ }), b[3:], nil
+ case '+', '-':
+ return p.scanIntOrFloat(b)
+ }
+
+ if len(b) < 3 {
+ return p.scanIntOrFloat(b)
+ }
+
+ s := 5
+ if len(b) < s {
+ s = len(b)
+ }
+
+ for idx, c := range b[:s] {
+ if isDigit(c) {
+ continue
+ }
+
+ if idx == 2 && c == ':' || (idx == 4 && c == '-') {
+ return p.scanDateTime(b)
+ }
+
+ break
+ }
+
+ return p.scanIntOrFloat(b)
+}
+
+func (p *Parser) scanDateTime(b []byte) (reference, []byte, error) {
+ // scans for contiguous characters in [0-9T:Z.+-], and up to one space if
+ // followed by a digit.
+ hasDate := false
+ hasTime := false
+ hasTz := false
+ seenSpace := false
+
+ i := 0
+byteLoop:
+ for ; i < len(b); i++ {
+ c := b[i]
+
+ switch {
+ case isDigit(c):
+ case c == '-':
+ hasDate = true
+ const minOffsetOfTz = 8
+ if i >= minOffsetOfTz {
+ hasTz = true
+ }
+ case c == 'T' || c == 't' || c == ':' || c == '.':
+ hasTime = true
+ case c == '+' || c == '-' || c == 'Z' || c == 'z':
+ hasTz = true
+ case c == ' ':
+ if !seenSpace && i+1 < len(b) && isDigit(b[i+1]) {
+ i += 2
+ // Avoid reaching past the end of the document in case the time
+ // is malformed. See TestIssue585.
+ if i >= len(b) {
+ i--
+ }
+ seenSpace = true
+ hasTime = true
+ } else {
+ break byteLoop
+ }
+ default:
+ break byteLoop
+ }
+ }
+
+ var kind Kind
+
+ if hasTime {
+ if hasDate {
+ if hasTz {
+ kind = DateTime
+ } else {
+ kind = LocalDateTime
+ }
+ } else {
+ kind = LocalTime
+ }
+ } else {
+ kind = LocalDate
+ }
+
+ return p.builder.Push(Node{
+ Kind: kind,
+ Data: b[:i],
+ }), b[i:], nil
+}
+
+//nolint:funlen,gocognit,cyclop
+func (p *Parser) scanIntOrFloat(b []byte) (reference, []byte, error) {
+ i := 0
+
+ if len(b) > 2 && b[0] == '0' && b[1] != '.' && b[1] != 'e' && b[1] != 'E' {
+ var isValidRune validRuneFn
+
+ switch b[1] {
+ case 'x':
+ isValidRune = isValidHexRune
+ case 'o':
+ isValidRune = isValidOctalRune
+ case 'b':
+ isValidRune = isValidBinaryRune
+ default:
+ i++
+ }
+
+ if isValidRune != nil {
+ i += 2
+ for ; i < len(b); i++ {
+ if !isValidRune(b[i]) {
+ break
+ }
+ }
+ }
+
+ return p.builder.Push(Node{
+ Kind: Integer,
+ Data: b[:i],
+ Raw: p.Range(b[:i]),
+ }), b[i:], nil
+ }
+
+ isFloat := false
+
+ for ; i < len(b); i++ {
+ c := b[i]
+
+ if c >= '0' && c <= '9' || c == '+' || c == '-' || c == '_' {
+ continue
+ }
+
+ if c == '.' || c == 'e' || c == 'E' {
+ isFloat = true
+
+ continue
+ }
+
+ if c == 'i' {
+ if scanFollowsInf(b[i:]) {
+ return p.builder.Push(Node{
+ Kind: Float,
+ Data: b[:i+3],
+ Raw: p.Range(b[:i+3]),
+ }), b[i+3:], nil
+ }
+
+ return invalidReference, nil, NewParserError(b[i:i+1], "unexpected character 'i' while scanning for a number")
+ }
+
+ if c == 'n' {
+ if scanFollowsNan(b[i:]) {
+ return p.builder.Push(Node{
+ Kind: Float,
+ Data: b[:i+3],
+ Raw: p.Range(b[:i+3]),
+ }), b[i+3:], nil
+ }
+
+ return invalidReference, nil, NewParserError(b[i:i+1], "unexpected character 'n' while scanning for a number")
+ }
+
+ break
+ }
+
+ if i == 0 {
+ return invalidReference, b, NewParserError(b, "incomplete number")
+ }
+
+ kind := Integer
+
+ if isFloat {
+ kind = Float
+ }
+
+ return p.builder.Push(Node{
+ Kind: kind,
+ Data: b[:i],
+ Raw: p.Range(b[:i]),
+ }), b[i:], nil
+}
+
+func isDigit(r byte) bool {
+ return r >= '0' && r <= '9'
+}
+
+type validRuneFn func(r byte) bool
+
+func isValidHexRune(r byte) bool {
+ return r >= 'a' && r <= 'f' ||
+ r >= 'A' && r <= 'F' ||
+ r >= '0' && r <= '9' ||
+ r == '_'
+}
+
+func isValidOctalRune(r byte) bool {
+ return r >= '0' && r <= '7' || r == '_'
+}
+
+func isValidBinaryRune(r byte) bool {
+ return r == '0' || r == '1' || r == '_'
+}
+
+func expect(x byte, b []byte) ([]byte, error) {
+ if len(b) == 0 {
+ return nil, NewParserError(b, "expected character %c but the document ended here", x)
+ }
+
+ if b[0] != x {
+ return nil, NewParserError(b[0:1], "expected character %c", x)
+ }
+
+ return b[1:], nil
+}
diff --git a/vendor/github.com/pelletier/go-toml/v2/unstable/scanner.go b/vendor/github.com/pelletier/go-toml/v2/unstable/scanner.go
new file mode 100644
index 00000000..0512181d
--- /dev/null
+++ b/vendor/github.com/pelletier/go-toml/v2/unstable/scanner.go
@@ -0,0 +1,270 @@
+package unstable
+
+import "github.com/pelletier/go-toml/v2/internal/characters"
+
+func scanFollows(b []byte, pattern string) bool {
+ n := len(pattern)
+
+ return len(b) >= n && string(b[:n]) == pattern
+}
+
+func scanFollowsMultilineBasicStringDelimiter(b []byte) bool {
+ return scanFollows(b, `"""`)
+}
+
+func scanFollowsMultilineLiteralStringDelimiter(b []byte) bool {
+ return scanFollows(b, `'''`)
+}
+
+func scanFollowsTrue(b []byte) bool {
+ return scanFollows(b, `true`)
+}
+
+func scanFollowsFalse(b []byte) bool {
+ return scanFollows(b, `false`)
+}
+
+func scanFollowsInf(b []byte) bool {
+ return scanFollows(b, `inf`)
+}
+
+func scanFollowsNan(b []byte) bool {
+ return scanFollows(b, `nan`)
+}
+
+func scanUnquotedKey(b []byte) ([]byte, []byte) {
+ // unquoted-key = 1*( ALPHA / DIGIT / %x2D / %x5F ) ; A-Z / a-z / 0-9 / - / _
+ for i := 0; i < len(b); i++ {
+ if !isUnquotedKeyChar(b[i]) {
+ return b[:i], b[i:]
+ }
+ }
+
+ return b, b[len(b):]
+}
+
+func isUnquotedKeyChar(r byte) bool {
+ return (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') || r == '-' || r == '_'
+}
+
+func scanLiteralString(b []byte) ([]byte, []byte, error) {
+ // literal-string = apostrophe *literal-char apostrophe
+ // apostrophe = %x27 ; ' apostrophe
+ // literal-char = %x09 / %x20-26 / %x28-7E / non-ascii
+ for i := 1; i < len(b); {
+ switch b[i] {
+ case '\'':
+ return b[:i+1], b[i+1:], nil
+ case '\n', '\r':
+ return nil, nil, NewParserError(b[i:i+1], "literal strings cannot have new lines")
+ }
+ size := characters.Utf8ValidNext(b[i:])
+ if size == 0 {
+ return nil, nil, NewParserError(b[i:i+1], "invalid character")
+ }
+ i += size
+ }
+
+ return nil, nil, NewParserError(b[len(b):], "unterminated literal string")
+}
+
+func scanMultilineLiteralString(b []byte) ([]byte, []byte, error) {
+ // ml-literal-string = ml-literal-string-delim [ newline ] ml-literal-body
+ // ml-literal-string-delim
+ // ml-literal-string-delim = 3apostrophe
+ // ml-literal-body = *mll-content *( mll-quotes 1*mll-content ) [ mll-quotes ]
+ //
+ // mll-content = mll-char / newline
+ // mll-char = %x09 / %x20-26 / %x28-7E / non-ascii
+ // mll-quotes = 1*2apostrophe
+ for i := 3; i < len(b); {
+ switch b[i] {
+ case '\'':
+ if scanFollowsMultilineLiteralStringDelimiter(b[i:]) {
+ i += 3
+
+ // At that point we found 3 apostrophe, and i is the
+ // index of the byte after the third one. The scanner
+ // needs to be eager, because there can be an extra 2
+ // apostrophe that can be accepted at the end of the
+ // string.
+
+ if i >= len(b) || b[i] != '\'' {
+ return b[:i], b[i:], nil
+ }
+ i++
+
+ if i >= len(b) || b[i] != '\'' {
+ return b[:i], b[i:], nil
+ }
+ i++
+
+ if i < len(b) && b[i] == '\'' {
+ return nil, nil, NewParserError(b[i-3:i+1], "''' not allowed in multiline literal string")
+ }
+
+ return b[:i], b[i:], nil
+ }
+ case '\r':
+ if len(b) < i+2 {
+ return nil, nil, NewParserError(b[len(b):], `need a \n after \r`)
+ }
+ if b[i+1] != '\n' {
+ return nil, nil, NewParserError(b[i:i+2], `need a \n after \r`)
+ }
+ i += 2 // skip the \n
+ continue
+ }
+ size := characters.Utf8ValidNext(b[i:])
+ if size == 0 {
+ return nil, nil, NewParserError(b[i:i+1], "invalid character")
+ }
+ i += size
+ }
+
+ return nil, nil, NewParserError(b[len(b):], `multiline literal string not terminated by '''`)
+}
+
+func scanWindowsNewline(b []byte) ([]byte, []byte, error) {
+ const lenCRLF = 2
+ if len(b) < lenCRLF {
+ return nil, nil, NewParserError(b, "windows new line expected")
+ }
+
+ if b[1] != '\n' {
+ return nil, nil, NewParserError(b, `windows new line should be \r\n`)
+ }
+
+ return b[:lenCRLF], b[lenCRLF:], nil
+}
+
+func scanWhitespace(b []byte) ([]byte, []byte) {
+ for i := 0; i < len(b); i++ {
+ switch b[i] {
+ case ' ', '\t':
+ continue
+ default:
+ return b[:i], b[i:]
+ }
+ }
+
+ return b, b[len(b):]
+}
+
+func scanComment(b []byte) ([]byte, []byte, error) {
+ // comment-start-symbol = %x23 ; #
+ // non-ascii = %x80-D7FF / %xE000-10FFFF
+ // non-eol = %x09 / %x20-7F / non-ascii
+ //
+ // comment = comment-start-symbol *non-eol
+
+ for i := 1; i < len(b); {
+ if b[i] == '\n' {
+ return b[:i], b[i:], nil
+ }
+ if b[i] == '\r' {
+ if i+1 < len(b) && b[i+1] == '\n' {
+ return b[:i+1], b[i+1:], nil
+ }
+ return nil, nil, NewParserError(b[i:i+1], "invalid character in comment")
+ }
+ size := characters.Utf8ValidNext(b[i:])
+ if size == 0 {
+ return nil, nil, NewParserError(b[i:i+1], "invalid character in comment")
+ }
+
+ i += size
+ }
+
+ return b, b[len(b):], nil
+}
+
+func scanBasicString(b []byte) ([]byte, bool, []byte, error) {
+ // basic-string = quotation-mark *basic-char quotation-mark
+ // quotation-mark = %x22 ; "
+ // basic-char = basic-unescaped / escaped
+ // basic-unescaped = wschar / %x21 / %x23-5B / %x5D-7E / non-ascii
+ // escaped = escape escape-seq-char
+ escaped := false
+ i := 1
+
+ for ; i < len(b); i++ {
+ switch b[i] {
+ case '"':
+ return b[:i+1], escaped, b[i+1:], nil
+ case '\n', '\r':
+ return nil, escaped, nil, NewParserError(b[i:i+1], "basic strings cannot have new lines")
+ case '\\':
+ if len(b) < i+2 {
+ return nil, escaped, nil, NewParserError(b[i:i+1], "need a character after \\")
+ }
+ escaped = true
+ i++ // skip the next character
+ }
+ }
+
+ return nil, escaped, nil, NewParserError(b[len(b):], `basic string not terminated by "`)
+}
+
+func scanMultilineBasicString(b []byte) ([]byte, bool, []byte, error) {
+ // ml-basic-string = ml-basic-string-delim [ newline ] ml-basic-body
+ // ml-basic-string-delim
+ // ml-basic-string-delim = 3quotation-mark
+ // ml-basic-body = *mlb-content *( mlb-quotes 1*mlb-content ) [ mlb-quotes ]
+ //
+ // mlb-content = mlb-char / newline / mlb-escaped-nl
+ // mlb-char = mlb-unescaped / escaped
+ // mlb-quotes = 1*2quotation-mark
+ // mlb-unescaped = wschar / %x21 / %x23-5B / %x5D-7E / non-ascii
+ // mlb-escaped-nl = escape ws newline *( wschar / newline )
+
+ escaped := false
+ i := 3
+
+ for ; i < len(b); i++ {
+ switch b[i] {
+ case '"':
+ if scanFollowsMultilineBasicStringDelimiter(b[i:]) {
+ i += 3
+
+ // At that point we found 3 apostrophe, and i is the
+ // index of the byte after the third one. The scanner
+ // needs to be eager, because there can be an extra 2
+ // apostrophe that can be accepted at the end of the
+ // string.
+
+ if i >= len(b) || b[i] != '"' {
+ return b[:i], escaped, b[i:], nil
+ }
+ i++
+
+ if i >= len(b) || b[i] != '"' {
+ return b[:i], escaped, b[i:], nil
+ }
+ i++
+
+ if i < len(b) && b[i] == '"' {
+ return nil, escaped, nil, NewParserError(b[i-3:i+1], `""" not allowed in multiline basic string`)
+ }
+
+ return b[:i], escaped, b[i:], nil
+ }
+ case '\\':
+ if len(b) < i+2 {
+ return nil, escaped, nil, NewParserError(b[len(b):], "need a character after \\")
+ }
+ escaped = true
+ i++ // skip the next character
+ case '\r':
+ if len(b) < i+2 {
+ return nil, escaped, nil, NewParserError(b[len(b):], `need a \n after \r`)
+ }
+ if b[i+1] != '\n' {
+ return nil, escaped, nil, NewParserError(b[i:i+2], `need a \n after \r`)
+ }
+ i++ // skip the \n
+ }
+ }
+
+ return nil, escaped, nil, NewParserError(b[len(b):], `multiline basic string not terminated by """`)
+}
diff --git a/vendor/github.com/pelletier/go-toml/v2/unstable/unmarshaler.go b/vendor/github.com/pelletier/go-toml/v2/unstable/unmarshaler.go
new file mode 100644
index 00000000..00cfd6de
--- /dev/null
+++ b/vendor/github.com/pelletier/go-toml/v2/unstable/unmarshaler.go
@@ -0,0 +1,7 @@
+package unstable
+
+// The Unmarshaler interface may be implemented by types to customize their
+// behavior when being unmarshaled from a TOML document.
+type Unmarshaler interface {
+ UnmarshalTOML(value *Node) error
+}
diff --git a/vendor/github.com/pjbgf/sha1cd/Dockerfile.arm b/vendor/github.com/pjbgf/sha1cd/Dockerfile.arm
new file mode 100644
index 00000000..99761296
--- /dev/null
+++ b/vendor/github.com/pjbgf/sha1cd/Dockerfile.arm
@@ -0,0 +1,23 @@
+FROM golang:1.20@sha256:2edf6aab2d57644f3fe7407132a0d1770846867465a39c2083770cf62734b05d
+
+ENV GOOS=linux
+ENV GOARCH=arm
+ENV CGO_ENABLED=1
+ENV CC=arm-linux-gnueabihf-gcc
+ENV PATH="/go/bin/${GOOS}_${GOARCH}:${PATH}"
+ENV PKG_CONFIG_PATH=/usr/lib/arm-linux-gnueabihf/pkgconfig
+
+RUN dpkg --add-architecture armhf \
+ && apt update \
+ && apt install -y --no-install-recommends \
+ upx \
+ gcc-arm-linux-gnueabihf \
+ libc6-dev-armhf-cross \
+ pkg-config \
+ && rm -rf /var/lib/apt/lists/*
+
+COPY . /src/workdir
+
+WORKDIR /src/workdir
+
+RUN go build ./...
diff --git a/vendor/github.com/pjbgf/sha1cd/Dockerfile.arm64 b/vendor/github.com/pjbgf/sha1cd/Dockerfile.arm64
new file mode 100644
index 00000000..66bd0947
--- /dev/null
+++ b/vendor/github.com/pjbgf/sha1cd/Dockerfile.arm64
@@ -0,0 +1,23 @@
+FROM golang:1.20@sha256:2edf6aab2d57644f3fe7407132a0d1770846867465a39c2083770cf62734b05d
+
+ENV GOOS=linux
+ENV GOARCH=arm64
+ENV CGO_ENABLED=1
+ENV CC=aarch64-linux-gnu-gcc
+ENV PATH="/go/bin/${GOOS}_${GOARCH}:${PATH}"
+ENV PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig
+
+# install build & runtime dependencies
+RUN dpkg --add-architecture arm64 \
+ && apt update \
+ && apt install -y --no-install-recommends \
+ gcc-aarch64-linux-gnu \
+ libc6-dev-arm64-cross \
+ pkg-config \
+ && rm -rf /var/lib/apt/lists/*
+
+COPY . /src/workdir
+
+WORKDIR /src/workdir
+
+RUN go build ./...
diff --git a/vendor/github.com/pjbgf/sha1cd/LICENSE b/vendor/github.com/pjbgf/sha1cd/LICENSE
new file mode 100644
index 00000000..261eeb9e
--- /dev/null
+++ b/vendor/github.com/pjbgf/sha1cd/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/vendor/github.com/pjbgf/sha1cd/Makefile b/vendor/github.com/pjbgf/sha1cd/Makefile
new file mode 100644
index 00000000..b24f2cba
--- /dev/null
+++ b/vendor/github.com/pjbgf/sha1cd/Makefile
@@ -0,0 +1,40 @@
+FUZZ_TIME ?= 1m
+
+export CGO_ENABLED := 1
+
+.PHONY: test
+test:
+ go test ./...
+
+.PHONY: bench
+bench:
+ go test -benchmem -run=^$$ -bench ^Benchmark ./...
+
+.PHONY: fuzz
+fuzz:
+ go test -tags gofuzz -fuzz=. -fuzztime=$(FUZZ_TIME) ./test/
+
+# Cross build project in arm/v7.
+build-arm:
+ docker build -t sha1cd-arm -f Dockerfile.arm .
+ docker run --rm sha1cd-arm
+
+# Cross build project in arm64.
+build-arm64:
+ docker build -t sha1cd-arm64 -f Dockerfile.arm64 .
+ docker run --rm sha1cd-arm64
+
+# Build with cgo disabled.
+build-nocgo:
+ CGO_ENABLED=0 go build ./cgo
+
+# Run cross-compilation to assure supported architectures.
+cross-build: build-arm build-arm64 build-nocgo
+
+generate:
+ go run sha1cdblock_amd64_asm.go -out sha1cdblock_amd64.s
+ sed -i 's;&\samd64;&\n// +build !noasm,gc,amd64;g' sha1cdblock_amd64.s
+
+verify: generate
+ git diff --exit-code
+ go vet ./...
diff --git a/vendor/github.com/pjbgf/sha1cd/README.md b/vendor/github.com/pjbgf/sha1cd/README.md
new file mode 100644
index 00000000..378cf78c
--- /dev/null
+++ b/vendor/github.com/pjbgf/sha1cd/README.md
@@ -0,0 +1,58 @@
+# sha1cd
+
+A Go implementation of SHA1 with counter-cryptanalysis, which detects
+collision attacks.
+
+The `cgo/lib` code is a carbon copy of the [original code], based on
+the award winning [white paper] by Marc Stevens.
+
+The Go implementation is largely based off Go's generic sha1.
+At present no SIMD optimisations have been implemented.
+
+## Usage
+
+`sha1cd` can be used as a drop-in replacement for `crypto/sha1`:
+
+```golang
+import "github.com/pjbgf/sha1cd"
+
+func test(){
+ data := []byte("data to be sha1 hashed")
+ h := sha1cd.Sum(data)
+ fmt.Printf("hash: %q\n", hex.EncodeToString(h))
+}
+```
+
+To obtain information as to whether a collision was found, use the
+func `CollisionResistantSum`.
+
+```golang
+import "github.com/pjbgf/sha1cd"
+
+func test(){
+ data := []byte("data to be sha1 hashed")
+ h, col := sha1cd.CollisionResistantSum(data)
+ if col {
+ fmt.Println("collision found!")
+ }
+ fmt.Printf("hash: %q", hex.EncodeToString(h))
+}
+```
+
+Note that the algorithm will automatically avoid collision, by
+extending the SHA1 to 240-steps, instead of 80 when a collision
+attempt is detected. Therefore, inputs that contains the unavoidable
+bit conditions will yield a different hash from `sha1cd`, when compared
+with results using `crypto/sha1`. Valid inputs will have matching the outputs.
+
+## References
+- https://shattered.io/
+- https://github.com/cr-marcstevens/sha1collisiondetection
+- https://csrc.nist.gov/Projects/Cryptographic-Algorithm-Validation-Program/Secure-Hashing#shavs
+
+## Use of the Original Implementation
+- https://github.com/git/git/commit/28dc98e343ca4eb370a29ceec4c19beac9b5c01e
+- https://github.com/libgit2/libgit2/pull/4136
+
+[original code]: https://github.com/cr-marcstevens/sha1collisiondetection
+[white paper]: https://marc-stevens.nl/research/papers/C13-S.pdf
diff --git a/vendor/github.com/pjbgf/sha1cd/detection.go b/vendor/github.com/pjbgf/sha1cd/detection.go
new file mode 100644
index 00000000..a1458748
--- /dev/null
+++ b/vendor/github.com/pjbgf/sha1cd/detection.go
@@ -0,0 +1,11 @@
+package sha1cd
+
+import "hash"
+
+type CollisionResistantHash interface {
+ // CollisionResistantSum extends on Sum by returning an additional boolean
+ // which indicates whether a collision was found during the hashing process.
+ CollisionResistantSum(b []byte) ([]byte, bool)
+
+ hash.Hash
+}
diff --git a/vendor/github.com/pjbgf/sha1cd/internal/const.go b/vendor/github.com/pjbgf/sha1cd/internal/const.go
new file mode 100644
index 00000000..944a131d
--- /dev/null
+++ b/vendor/github.com/pjbgf/sha1cd/internal/const.go
@@ -0,0 +1,42 @@
+package shared
+
+const (
+ // Constants for the SHA-1 hash function.
+ K0 = 0x5A827999
+ K1 = 0x6ED9EBA1
+ K2 = 0x8F1BBCDC
+ K3 = 0xCA62C1D6
+
+ // Initial values for the buffer variables: h0, h1, h2, h3, h4.
+ Init0 = 0x67452301
+ Init1 = 0xEFCDAB89
+ Init2 = 0x98BADCFE
+ Init3 = 0x10325476
+ Init4 = 0xC3D2E1F0
+
+ // Initial values for the temporary variables (ihvtmp0, ihvtmp1, ihvtmp2, ihvtmp3, ihvtmp4) during the SHA recompression step.
+ InitTmp0 = 0xD5
+ InitTmp1 = 0x394
+ InitTmp2 = 0x8152A8
+ InitTmp3 = 0x0
+ InitTmp4 = 0xA7ECE0
+
+ // SHA1 contains 2 buffers, each based off 5 32-bit words.
+ WordBuffers = 5
+
+ // The output of SHA1 is 20 bytes (160 bits).
+ Size = 20
+
+ // Rounds represents the number of steps required to process each chunk.
+ Rounds = 80
+
+ // SHA1 processes the input data in chunks. Each chunk contains 64 bytes.
+ Chunk = 64
+
+ // The number of pre-step compression state to store.
+ // Currently there are 3 pre-step compression states required: 0, 58, 65.
+ PreStepState = 3
+
+ Magic = "shacd\x01"
+ MarshaledSize = len(Magic) + 5*4 + Chunk + 8
+)
diff --git a/vendor/github.com/pjbgf/sha1cd/sha1cd.go b/vendor/github.com/pjbgf/sha1cd/sha1cd.go
new file mode 100644
index 00000000..a69e480e
--- /dev/null
+++ b/vendor/github.com/pjbgf/sha1cd/sha1cd.go
@@ -0,0 +1,227 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package sha1cd implements collision detection based on the whitepaper
+// Counter-cryptanalysis from Marc Stevens. The original ubc implementation
+// was done by Marc Stevens and Dan Shumow, and can be found at:
+// https://github.com/cr-marcstevens/sha1collisiondetection
+package sha1cd
+
+// This SHA1 implementation is based on Go's generic SHA1.
+// Original: https://github.com/golang/go/blob/master/src/crypto/sha1/sha1.go
+
+import (
+ "crypto"
+ "encoding/binary"
+ "errors"
+ "hash"
+
+ shared "github.com/pjbgf/sha1cd/internal"
+)
+
+func init() {
+ crypto.RegisterHash(crypto.SHA1, New)
+}
+
+// The size of a SHA-1 checksum in bytes.
+const Size = shared.Size
+
+// The blocksize of SHA-1 in bytes.
+const BlockSize = shared.Chunk
+
+// digest represents the partial evaluation of a checksum.
+type digest struct {
+ h [shared.WordBuffers]uint32
+ x [shared.Chunk]byte
+ nx int
+ len uint64
+
+ // col defines whether a collision has been found.
+ col bool
+ blockFunc func(dig *digest, p []byte)
+}
+
+func (d *digest) MarshalBinary() ([]byte, error) {
+ b := make([]byte, 0, shared.MarshaledSize)
+ b = append(b, shared.Magic...)
+ b = appendUint32(b, d.h[0])
+ b = appendUint32(b, d.h[1])
+ b = appendUint32(b, d.h[2])
+ b = appendUint32(b, d.h[3])
+ b = appendUint32(b, d.h[4])
+ b = append(b, d.x[:d.nx]...)
+ b = b[:len(b)+len(d.x)-d.nx] // already zero
+ b = appendUint64(b, d.len)
+ return b, nil
+}
+
+func appendUint32(b []byte, v uint32) []byte {
+ return append(b,
+ byte(v>>24),
+ byte(v>>16),
+ byte(v>>8),
+ byte(v),
+ )
+}
+
+func appendUint64(b []byte, v uint64) []byte {
+ return append(b,
+ byte(v>>56),
+ byte(v>>48),
+ byte(v>>40),
+ byte(v>>32),
+ byte(v>>24),
+ byte(v>>16),
+ byte(v>>8),
+ byte(v),
+ )
+}
+
+func (d *digest) UnmarshalBinary(b []byte) error {
+ if len(b) < len(shared.Magic) || string(b[:len(shared.Magic)]) != shared.Magic {
+ return errors.New("crypto/sha1: invalid hash state identifier")
+ }
+ if len(b) != shared.MarshaledSize {
+ return errors.New("crypto/sha1: invalid hash state size")
+ }
+ b = b[len(shared.Magic):]
+ b, d.h[0] = consumeUint32(b)
+ b, d.h[1] = consumeUint32(b)
+ b, d.h[2] = consumeUint32(b)
+ b, d.h[3] = consumeUint32(b)
+ b, d.h[4] = consumeUint32(b)
+ b = b[copy(d.x[:], b):]
+ b, d.len = consumeUint64(b)
+ d.nx = int(d.len % shared.Chunk)
+ return nil
+}
+
+func consumeUint64(b []byte) ([]byte, uint64) {
+ _ = b[7]
+ x := uint64(b[7]) | uint64(b[6])<<8 | uint64(b[shared.WordBuffers])<<16 | uint64(b[4])<<24 |
+ uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56
+ return b[8:], x
+}
+
+func consumeUint32(b []byte) ([]byte, uint32) {
+ _ = b[3]
+ x := uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24
+ return b[4:], x
+}
+
+func (d *digest) Reset() {
+ d.h[0] = shared.Init0
+ d.h[1] = shared.Init1
+ d.h[2] = shared.Init2
+ d.h[3] = shared.Init3
+ d.h[4] = shared.Init4
+ d.nx = 0
+ d.len = 0
+
+ d.col = false
+}
+
+// New returns a new hash.Hash computing the SHA1 checksum. The Hash also
+// implements encoding.BinaryMarshaler and encoding.BinaryUnmarshaler to
+// marshal and unmarshal the internal state of the hash.
+func New() hash.Hash {
+ d := new(digest)
+
+ d.blockFunc = block
+ d.Reset()
+ return d
+}
+
+// NewGeneric is equivalent to New but uses the Go generic implementation,
+// avoiding any processor-specific optimizations.
+func NewGeneric() hash.Hash {
+ d := new(digest)
+
+ d.blockFunc = blockGeneric
+ d.Reset()
+ return d
+}
+
+func (d *digest) Size() int { return Size }
+
+func (d *digest) BlockSize() int { return BlockSize }
+
+func (d *digest) Write(p []byte) (nn int, err error) {
+ if len(p) == 0 {
+ return
+ }
+
+ nn = len(p)
+ d.len += uint64(nn)
+ if d.nx > 0 {
+ n := copy(d.x[d.nx:], p)
+ d.nx += n
+ if d.nx == shared.Chunk {
+ d.blockFunc(d, d.x[:])
+ d.nx = 0
+ }
+ p = p[n:]
+ }
+ if len(p) >= shared.Chunk {
+ n := len(p) &^ (shared.Chunk - 1)
+ d.blockFunc(d, p[:n])
+ p = p[n:]
+ }
+ if len(p) > 0 {
+ d.nx = copy(d.x[:], p)
+ }
+ return
+}
+
+func (d *digest) Sum(in []byte) []byte {
+ // Make a copy of d so that caller can keep writing and summing.
+ d0 := *d
+ hash := d0.checkSum()
+ return append(in, hash[:]...)
+}
+
+func (d *digest) checkSum() [Size]byte {
+ len := d.len
+ // Padding. Add a 1 bit and 0 bits until 56 bytes mod 64.
+ var tmp [64]byte
+ tmp[0] = 0x80
+ if len%64 < 56 {
+ d.Write(tmp[0 : 56-len%64])
+ } else {
+ d.Write(tmp[0 : 64+56-len%64])
+ }
+
+ // Length in bits.
+ len <<= 3
+ binary.BigEndian.PutUint64(tmp[:], len)
+ d.Write(tmp[0:8])
+
+ if d.nx != 0 {
+ panic("d.nx != 0")
+ }
+
+ var digest [Size]byte
+
+ binary.BigEndian.PutUint32(digest[0:], d.h[0])
+ binary.BigEndian.PutUint32(digest[4:], d.h[1])
+ binary.BigEndian.PutUint32(digest[8:], d.h[2])
+ binary.BigEndian.PutUint32(digest[12:], d.h[3])
+ binary.BigEndian.PutUint32(digest[16:], d.h[4])
+
+ return digest
+}
+
+// Sum returns the SHA-1 checksum of the data.
+func Sum(data []byte) ([Size]byte, bool) {
+ d := New().(*digest)
+ d.Write(data)
+ return d.checkSum(), d.col
+}
+
+func (d *digest) CollisionResistantSum(in []byte) ([]byte, bool) {
+ // Make a copy of d so that caller can keep writing and summing.
+ d0 := *d
+ hash := d0.checkSum()
+ return append(in, hash[:]...), d0.col
+}
diff --git a/vendor/github.com/pjbgf/sha1cd/sha1cdblock_amd64.go b/vendor/github.com/pjbgf/sha1cd/sha1cdblock_amd64.go
new file mode 100644
index 00000000..95e08308
--- /dev/null
+++ b/vendor/github.com/pjbgf/sha1cd/sha1cdblock_amd64.go
@@ -0,0 +1,50 @@
+//go:build !noasm && gc && amd64
+// +build !noasm,gc,amd64
+
+package sha1cd
+
+import (
+ "math"
+ "unsafe"
+
+ shared "github.com/pjbgf/sha1cd/internal"
+)
+
+type sliceHeader struct {
+ base uintptr
+ len int
+ cap int
+}
+
+// blockAMD64 hashes the message p into the current state in dig.
+// Both m1 and cs are used to store intermediate results which are used by the collision detection logic.
+//
+//go:noescape
+func blockAMD64(dig *digest, p sliceHeader, m1 []uint32, cs [][5]uint32)
+
+func block(dig *digest, p []byte) {
+ m1 := [shared.Rounds]uint32{}
+ cs := [shared.PreStepState][shared.WordBuffers]uint32{}
+
+ for len(p) >= shared.Chunk {
+ // Only send a block to be processed, as the collission detection
+ // works on a block by block basis.
+ ips := sliceHeader{
+ base: uintptr(unsafe.Pointer(&p[0])),
+ len: int(math.Min(float64(len(p)), float64(shared.Chunk))),
+ cap: shared.Chunk,
+ }
+
+ blockAMD64(dig, ips, m1[:], cs[:])
+
+ col := checkCollision(m1, cs, dig.h)
+ if col {
+ dig.col = true
+
+ blockAMD64(dig, ips, m1[:], cs[:])
+ blockAMD64(dig, ips, m1[:], cs[:])
+ }
+
+ p = p[shared.Chunk:]
+ }
+}
diff --git a/vendor/github.com/pjbgf/sha1cd/sha1cdblock_amd64.s b/vendor/github.com/pjbgf/sha1cd/sha1cdblock_amd64.s
new file mode 100644
index 00000000..86f9821c
--- /dev/null
+++ b/vendor/github.com/pjbgf/sha1cd/sha1cdblock_amd64.s
@@ -0,0 +1,2274 @@
+// Code generated by command: go run sha1cdblock_amd64_asm.go -out sha1cdblock_amd64.s. DO NOT EDIT.
+
+//go:build !noasm && gc && amd64
+// +build !noasm,gc,amd64
+
+#include "textflag.h"
+
+// func blockAMD64(dig *digest, p []byte, m1 []uint32, cs [][5]uint32)
+TEXT ·blockAMD64(SB), NOSPLIT, $64-80
+ MOVQ dig+0(FP), R8
+ MOVQ p_base+8(FP), DI
+ MOVQ p_len+16(FP), DX
+ SHRQ $+6, DX
+ SHLQ $+6, DX
+ LEAQ (DI)(DX*1), SI
+
+ // Load h0, h1, h2, h3, h4.
+ MOVL (R8), AX
+ MOVL 4(R8), BX
+ MOVL 8(R8), CX
+ MOVL 12(R8), DX
+ MOVL 16(R8), BP
+
+ // len(p) >= chunk
+ CMPQ DI, SI
+ JEQ end
+
+loop:
+ // Initialize registers a, b, c, d, e.
+ MOVL AX, R10
+ MOVL BX, R11
+ MOVL CX, R12
+ MOVL DX, R13
+ MOVL BP, R14
+
+ // ROUND1 (steps 0-15)
+ // Load cs
+ MOVQ cs_base+56(FP), R8
+ MOVL R10, (R8)
+ MOVL R11, 4(R8)
+ MOVL R12, 8(R8)
+ MOVL R13, 12(R8)
+ MOVL R14, 16(R8)
+
+ // ROUND1(0)
+ // LOAD
+ MOVL (DI), R9
+ BSWAPL R9
+ MOVL R9, (SP)
+
+ // FUNC1
+ MOVL R13, R15
+ XORL R12, R15
+ ANDL R11, R15
+ XORL R13, R15
+
+ // MIX
+ ROLL $+30, R11
+ ADDL R15, R14
+ MOVL R10, R8
+ ROLL $+5, R8
+ LEAL 1518500249(R14)(R9*1), R14
+ ADDL R8, R14
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL (SP), R9
+ MOVL R9, (R8)
+
+ // ROUND1(1)
+ // LOAD
+ MOVL 4(DI), R9
+ BSWAPL R9
+ MOVL R9, 4(SP)
+
+ // FUNC1
+ MOVL R12, R15
+ XORL R11, R15
+ ANDL R10, R15
+ XORL R12, R15
+
+ // MIX
+ ROLL $+30, R10
+ ADDL R15, R13
+ MOVL R14, R8
+ ROLL $+5, R8
+ LEAL 1518500249(R13)(R9*1), R13
+ ADDL R8, R13
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 4(SP), R9
+ MOVL R9, 4(R8)
+
+ // ROUND1(2)
+ // LOAD
+ MOVL 8(DI), R9
+ BSWAPL R9
+ MOVL R9, 8(SP)
+
+ // FUNC1
+ MOVL R11, R15
+ XORL R10, R15
+ ANDL R14, R15
+ XORL R11, R15
+
+ // MIX
+ ROLL $+30, R14
+ ADDL R15, R12
+ MOVL R13, R8
+ ROLL $+5, R8
+ LEAL 1518500249(R12)(R9*1), R12
+ ADDL R8, R12
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 8(SP), R9
+ MOVL R9, 8(R8)
+
+ // ROUND1(3)
+ // LOAD
+ MOVL 12(DI), R9
+ BSWAPL R9
+ MOVL R9, 12(SP)
+
+ // FUNC1
+ MOVL R10, R15
+ XORL R14, R15
+ ANDL R13, R15
+ XORL R10, R15
+
+ // MIX
+ ROLL $+30, R13
+ ADDL R15, R11
+ MOVL R12, R8
+ ROLL $+5, R8
+ LEAL 1518500249(R11)(R9*1), R11
+ ADDL R8, R11
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 12(SP), R9
+ MOVL R9, 12(R8)
+
+ // ROUND1(4)
+ // LOAD
+ MOVL 16(DI), R9
+ BSWAPL R9
+ MOVL R9, 16(SP)
+
+ // FUNC1
+ MOVL R14, R15
+ XORL R13, R15
+ ANDL R12, R15
+ XORL R14, R15
+
+ // MIX
+ ROLL $+30, R12
+ ADDL R15, R10
+ MOVL R11, R8
+ ROLL $+5, R8
+ LEAL 1518500249(R10)(R9*1), R10
+ ADDL R8, R10
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 16(SP), R9
+ MOVL R9, 16(R8)
+
+ // ROUND1(5)
+ // LOAD
+ MOVL 20(DI), R9
+ BSWAPL R9
+ MOVL R9, 20(SP)
+
+ // FUNC1
+ MOVL R13, R15
+ XORL R12, R15
+ ANDL R11, R15
+ XORL R13, R15
+
+ // MIX
+ ROLL $+30, R11
+ ADDL R15, R14
+ MOVL R10, R8
+ ROLL $+5, R8
+ LEAL 1518500249(R14)(R9*1), R14
+ ADDL R8, R14
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 20(SP), R9
+ MOVL R9, 20(R8)
+
+ // ROUND1(6)
+ // LOAD
+ MOVL 24(DI), R9
+ BSWAPL R9
+ MOVL R9, 24(SP)
+
+ // FUNC1
+ MOVL R12, R15
+ XORL R11, R15
+ ANDL R10, R15
+ XORL R12, R15
+
+ // MIX
+ ROLL $+30, R10
+ ADDL R15, R13
+ MOVL R14, R8
+ ROLL $+5, R8
+ LEAL 1518500249(R13)(R9*1), R13
+ ADDL R8, R13
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 24(SP), R9
+ MOVL R9, 24(R8)
+
+ // ROUND1(7)
+ // LOAD
+ MOVL 28(DI), R9
+ BSWAPL R9
+ MOVL R9, 28(SP)
+
+ // FUNC1
+ MOVL R11, R15
+ XORL R10, R15
+ ANDL R14, R15
+ XORL R11, R15
+
+ // MIX
+ ROLL $+30, R14
+ ADDL R15, R12
+ MOVL R13, R8
+ ROLL $+5, R8
+ LEAL 1518500249(R12)(R9*1), R12
+ ADDL R8, R12
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 28(SP), R9
+ MOVL R9, 28(R8)
+
+ // ROUND1(8)
+ // LOAD
+ MOVL 32(DI), R9
+ BSWAPL R9
+ MOVL R9, 32(SP)
+
+ // FUNC1
+ MOVL R10, R15
+ XORL R14, R15
+ ANDL R13, R15
+ XORL R10, R15
+
+ // MIX
+ ROLL $+30, R13
+ ADDL R15, R11
+ MOVL R12, R8
+ ROLL $+5, R8
+ LEAL 1518500249(R11)(R9*1), R11
+ ADDL R8, R11
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 32(SP), R9
+ MOVL R9, 32(R8)
+
+ // ROUND1(9)
+ // LOAD
+ MOVL 36(DI), R9
+ BSWAPL R9
+ MOVL R9, 36(SP)
+
+ // FUNC1
+ MOVL R14, R15
+ XORL R13, R15
+ ANDL R12, R15
+ XORL R14, R15
+
+ // MIX
+ ROLL $+30, R12
+ ADDL R15, R10
+ MOVL R11, R8
+ ROLL $+5, R8
+ LEAL 1518500249(R10)(R9*1), R10
+ ADDL R8, R10
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 36(SP), R9
+ MOVL R9, 36(R8)
+
+ // ROUND1(10)
+ // LOAD
+ MOVL 40(DI), R9
+ BSWAPL R9
+ MOVL R9, 40(SP)
+
+ // FUNC1
+ MOVL R13, R15
+ XORL R12, R15
+ ANDL R11, R15
+ XORL R13, R15
+
+ // MIX
+ ROLL $+30, R11
+ ADDL R15, R14
+ MOVL R10, R8
+ ROLL $+5, R8
+ LEAL 1518500249(R14)(R9*1), R14
+ ADDL R8, R14
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 40(SP), R9
+ MOVL R9, 40(R8)
+
+ // ROUND1(11)
+ // LOAD
+ MOVL 44(DI), R9
+ BSWAPL R9
+ MOVL R9, 44(SP)
+
+ // FUNC1
+ MOVL R12, R15
+ XORL R11, R15
+ ANDL R10, R15
+ XORL R12, R15
+
+ // MIX
+ ROLL $+30, R10
+ ADDL R15, R13
+ MOVL R14, R8
+ ROLL $+5, R8
+ LEAL 1518500249(R13)(R9*1), R13
+ ADDL R8, R13
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 44(SP), R9
+ MOVL R9, 44(R8)
+
+ // ROUND1(12)
+ // LOAD
+ MOVL 48(DI), R9
+ BSWAPL R9
+ MOVL R9, 48(SP)
+
+ // FUNC1
+ MOVL R11, R15
+ XORL R10, R15
+ ANDL R14, R15
+ XORL R11, R15
+
+ // MIX
+ ROLL $+30, R14
+ ADDL R15, R12
+ MOVL R13, R8
+ ROLL $+5, R8
+ LEAL 1518500249(R12)(R9*1), R12
+ ADDL R8, R12
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 48(SP), R9
+ MOVL R9, 48(R8)
+
+ // ROUND1(13)
+ // LOAD
+ MOVL 52(DI), R9
+ BSWAPL R9
+ MOVL R9, 52(SP)
+
+ // FUNC1
+ MOVL R10, R15
+ XORL R14, R15
+ ANDL R13, R15
+ XORL R10, R15
+
+ // MIX
+ ROLL $+30, R13
+ ADDL R15, R11
+ MOVL R12, R8
+ ROLL $+5, R8
+ LEAL 1518500249(R11)(R9*1), R11
+ ADDL R8, R11
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 52(SP), R9
+ MOVL R9, 52(R8)
+
+ // ROUND1(14)
+ // LOAD
+ MOVL 56(DI), R9
+ BSWAPL R9
+ MOVL R9, 56(SP)
+
+ // FUNC1
+ MOVL R14, R15
+ XORL R13, R15
+ ANDL R12, R15
+ XORL R14, R15
+
+ // MIX
+ ROLL $+30, R12
+ ADDL R15, R10
+ MOVL R11, R8
+ ROLL $+5, R8
+ LEAL 1518500249(R10)(R9*1), R10
+ ADDL R8, R10
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 56(SP), R9
+ MOVL R9, 56(R8)
+
+ // ROUND1(15)
+ // LOAD
+ MOVL 60(DI), R9
+ BSWAPL R9
+ MOVL R9, 60(SP)
+
+ // FUNC1
+ MOVL R13, R15
+ XORL R12, R15
+ ANDL R11, R15
+ XORL R13, R15
+
+ // MIX
+ ROLL $+30, R11
+ ADDL R15, R14
+ MOVL R10, R8
+ ROLL $+5, R8
+ LEAL 1518500249(R14)(R9*1), R14
+ ADDL R8, R14
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 60(SP), R9
+ MOVL R9, 60(R8)
+
+ // ROUND1x (steps 16-19) - same as ROUND1 but with no data load.
+ // ROUND1x(16)
+ // SHUFFLE
+ MOVL (SP), R9
+ XORL 52(SP), R9
+ XORL 32(SP), R9
+ XORL 8(SP), R9
+ ROLL $+1, R9
+ MOVL R9, (SP)
+
+ // FUNC1
+ MOVL R12, R15
+ XORL R11, R15
+ ANDL R10, R15
+ XORL R12, R15
+
+ // MIX
+ ROLL $+30, R10
+ ADDL R15, R13
+ MOVL R14, R8
+ ROLL $+5, R8
+ LEAL 1518500249(R13)(R9*1), R13
+ ADDL R8, R13
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL (SP), R9
+ MOVL R9, 64(R8)
+
+ // ROUND1x(17)
+ // SHUFFLE
+ MOVL 4(SP), R9
+ XORL 56(SP), R9
+ XORL 36(SP), R9
+ XORL 12(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 4(SP)
+
+ // FUNC1
+ MOVL R11, R15
+ XORL R10, R15
+ ANDL R14, R15
+ XORL R11, R15
+
+ // MIX
+ ROLL $+30, R14
+ ADDL R15, R12
+ MOVL R13, R8
+ ROLL $+5, R8
+ LEAL 1518500249(R12)(R9*1), R12
+ ADDL R8, R12
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 4(SP), R9
+ MOVL R9, 68(R8)
+
+ // ROUND1x(18)
+ // SHUFFLE
+ MOVL 8(SP), R9
+ XORL 60(SP), R9
+ XORL 40(SP), R9
+ XORL 16(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 8(SP)
+
+ // FUNC1
+ MOVL R10, R15
+ XORL R14, R15
+ ANDL R13, R15
+ XORL R10, R15
+
+ // MIX
+ ROLL $+30, R13
+ ADDL R15, R11
+ MOVL R12, R8
+ ROLL $+5, R8
+ LEAL 1518500249(R11)(R9*1), R11
+ ADDL R8, R11
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 8(SP), R9
+ MOVL R9, 72(R8)
+
+ // ROUND1x(19)
+ // SHUFFLE
+ MOVL 12(SP), R9
+ XORL (SP), R9
+ XORL 44(SP), R9
+ XORL 20(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 12(SP)
+
+ // FUNC1
+ MOVL R14, R15
+ XORL R13, R15
+ ANDL R12, R15
+ XORL R14, R15
+
+ // MIX
+ ROLL $+30, R12
+ ADDL R15, R10
+ MOVL R11, R8
+ ROLL $+5, R8
+ LEAL 1518500249(R10)(R9*1), R10
+ ADDL R8, R10
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 12(SP), R9
+ MOVL R9, 76(R8)
+
+ // ROUND2 (steps 20-39)
+ // ROUND2(20)
+ // SHUFFLE
+ MOVL 16(SP), R9
+ XORL 4(SP), R9
+ XORL 48(SP), R9
+ XORL 24(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 16(SP)
+
+ // FUNC2
+ MOVL R11, R15
+ XORL R12, R15
+ XORL R13, R15
+
+ // MIX
+ ROLL $+30, R11
+ ADDL R15, R14
+ MOVL R10, R8
+ ROLL $+5, R8
+ LEAL 1859775393(R14)(R9*1), R14
+ ADDL R8, R14
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 16(SP), R9
+ MOVL R9, 80(R8)
+
+ // ROUND2(21)
+ // SHUFFLE
+ MOVL 20(SP), R9
+ XORL 8(SP), R9
+ XORL 52(SP), R9
+ XORL 28(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 20(SP)
+
+ // FUNC2
+ MOVL R10, R15
+ XORL R11, R15
+ XORL R12, R15
+
+ // MIX
+ ROLL $+30, R10
+ ADDL R15, R13
+ MOVL R14, R8
+ ROLL $+5, R8
+ LEAL 1859775393(R13)(R9*1), R13
+ ADDL R8, R13
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 20(SP), R9
+ MOVL R9, 84(R8)
+
+ // ROUND2(22)
+ // SHUFFLE
+ MOVL 24(SP), R9
+ XORL 12(SP), R9
+ XORL 56(SP), R9
+ XORL 32(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 24(SP)
+
+ // FUNC2
+ MOVL R14, R15
+ XORL R10, R15
+ XORL R11, R15
+
+ // MIX
+ ROLL $+30, R14
+ ADDL R15, R12
+ MOVL R13, R8
+ ROLL $+5, R8
+ LEAL 1859775393(R12)(R9*1), R12
+ ADDL R8, R12
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 24(SP), R9
+ MOVL R9, 88(R8)
+
+ // ROUND2(23)
+ // SHUFFLE
+ MOVL 28(SP), R9
+ XORL 16(SP), R9
+ XORL 60(SP), R9
+ XORL 36(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 28(SP)
+
+ // FUNC2
+ MOVL R13, R15
+ XORL R14, R15
+ XORL R10, R15
+
+ // MIX
+ ROLL $+30, R13
+ ADDL R15, R11
+ MOVL R12, R8
+ ROLL $+5, R8
+ LEAL 1859775393(R11)(R9*1), R11
+ ADDL R8, R11
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 28(SP), R9
+ MOVL R9, 92(R8)
+
+ // ROUND2(24)
+ // SHUFFLE
+ MOVL 32(SP), R9
+ XORL 20(SP), R9
+ XORL (SP), R9
+ XORL 40(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 32(SP)
+
+ // FUNC2
+ MOVL R12, R15
+ XORL R13, R15
+ XORL R14, R15
+
+ // MIX
+ ROLL $+30, R12
+ ADDL R15, R10
+ MOVL R11, R8
+ ROLL $+5, R8
+ LEAL 1859775393(R10)(R9*1), R10
+ ADDL R8, R10
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 32(SP), R9
+ MOVL R9, 96(R8)
+
+ // ROUND2(25)
+ // SHUFFLE
+ MOVL 36(SP), R9
+ XORL 24(SP), R9
+ XORL 4(SP), R9
+ XORL 44(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 36(SP)
+
+ // FUNC2
+ MOVL R11, R15
+ XORL R12, R15
+ XORL R13, R15
+
+ // MIX
+ ROLL $+30, R11
+ ADDL R15, R14
+ MOVL R10, R8
+ ROLL $+5, R8
+ LEAL 1859775393(R14)(R9*1), R14
+ ADDL R8, R14
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 36(SP), R9
+ MOVL R9, 100(R8)
+
+ // ROUND2(26)
+ // SHUFFLE
+ MOVL 40(SP), R9
+ XORL 28(SP), R9
+ XORL 8(SP), R9
+ XORL 48(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 40(SP)
+
+ // FUNC2
+ MOVL R10, R15
+ XORL R11, R15
+ XORL R12, R15
+
+ // MIX
+ ROLL $+30, R10
+ ADDL R15, R13
+ MOVL R14, R8
+ ROLL $+5, R8
+ LEAL 1859775393(R13)(R9*1), R13
+ ADDL R8, R13
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 40(SP), R9
+ MOVL R9, 104(R8)
+
+ // ROUND2(27)
+ // SHUFFLE
+ MOVL 44(SP), R9
+ XORL 32(SP), R9
+ XORL 12(SP), R9
+ XORL 52(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 44(SP)
+
+ // FUNC2
+ MOVL R14, R15
+ XORL R10, R15
+ XORL R11, R15
+
+ // MIX
+ ROLL $+30, R14
+ ADDL R15, R12
+ MOVL R13, R8
+ ROLL $+5, R8
+ LEAL 1859775393(R12)(R9*1), R12
+ ADDL R8, R12
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 44(SP), R9
+ MOVL R9, 108(R8)
+
+ // ROUND2(28)
+ // SHUFFLE
+ MOVL 48(SP), R9
+ XORL 36(SP), R9
+ XORL 16(SP), R9
+ XORL 56(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 48(SP)
+
+ // FUNC2
+ MOVL R13, R15
+ XORL R14, R15
+ XORL R10, R15
+
+ // MIX
+ ROLL $+30, R13
+ ADDL R15, R11
+ MOVL R12, R8
+ ROLL $+5, R8
+ LEAL 1859775393(R11)(R9*1), R11
+ ADDL R8, R11
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 48(SP), R9
+ MOVL R9, 112(R8)
+
+ // ROUND2(29)
+ // SHUFFLE
+ MOVL 52(SP), R9
+ XORL 40(SP), R9
+ XORL 20(SP), R9
+ XORL 60(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 52(SP)
+
+ // FUNC2
+ MOVL R12, R15
+ XORL R13, R15
+ XORL R14, R15
+
+ // MIX
+ ROLL $+30, R12
+ ADDL R15, R10
+ MOVL R11, R8
+ ROLL $+5, R8
+ LEAL 1859775393(R10)(R9*1), R10
+ ADDL R8, R10
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 52(SP), R9
+ MOVL R9, 116(R8)
+
+ // ROUND2(30)
+ // SHUFFLE
+ MOVL 56(SP), R9
+ XORL 44(SP), R9
+ XORL 24(SP), R9
+ XORL (SP), R9
+ ROLL $+1, R9
+ MOVL R9, 56(SP)
+
+ // FUNC2
+ MOVL R11, R15
+ XORL R12, R15
+ XORL R13, R15
+
+ // MIX
+ ROLL $+30, R11
+ ADDL R15, R14
+ MOVL R10, R8
+ ROLL $+5, R8
+ LEAL 1859775393(R14)(R9*1), R14
+ ADDL R8, R14
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 56(SP), R9
+ MOVL R9, 120(R8)
+
+ // ROUND2(31)
+ // SHUFFLE
+ MOVL 60(SP), R9
+ XORL 48(SP), R9
+ XORL 28(SP), R9
+ XORL 4(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 60(SP)
+
+ // FUNC2
+ MOVL R10, R15
+ XORL R11, R15
+ XORL R12, R15
+
+ // MIX
+ ROLL $+30, R10
+ ADDL R15, R13
+ MOVL R14, R8
+ ROLL $+5, R8
+ LEAL 1859775393(R13)(R9*1), R13
+ ADDL R8, R13
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 60(SP), R9
+ MOVL R9, 124(R8)
+
+ // ROUND2(32)
+ // SHUFFLE
+ MOVL (SP), R9
+ XORL 52(SP), R9
+ XORL 32(SP), R9
+ XORL 8(SP), R9
+ ROLL $+1, R9
+ MOVL R9, (SP)
+
+ // FUNC2
+ MOVL R14, R15
+ XORL R10, R15
+ XORL R11, R15
+
+ // MIX
+ ROLL $+30, R14
+ ADDL R15, R12
+ MOVL R13, R8
+ ROLL $+5, R8
+ LEAL 1859775393(R12)(R9*1), R12
+ ADDL R8, R12
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL (SP), R9
+ MOVL R9, 128(R8)
+
+ // ROUND2(33)
+ // SHUFFLE
+ MOVL 4(SP), R9
+ XORL 56(SP), R9
+ XORL 36(SP), R9
+ XORL 12(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 4(SP)
+
+ // FUNC2
+ MOVL R13, R15
+ XORL R14, R15
+ XORL R10, R15
+
+ // MIX
+ ROLL $+30, R13
+ ADDL R15, R11
+ MOVL R12, R8
+ ROLL $+5, R8
+ LEAL 1859775393(R11)(R9*1), R11
+ ADDL R8, R11
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 4(SP), R9
+ MOVL R9, 132(R8)
+
+ // ROUND2(34)
+ // SHUFFLE
+ MOVL 8(SP), R9
+ XORL 60(SP), R9
+ XORL 40(SP), R9
+ XORL 16(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 8(SP)
+
+ // FUNC2
+ MOVL R12, R15
+ XORL R13, R15
+ XORL R14, R15
+
+ // MIX
+ ROLL $+30, R12
+ ADDL R15, R10
+ MOVL R11, R8
+ ROLL $+5, R8
+ LEAL 1859775393(R10)(R9*1), R10
+ ADDL R8, R10
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 8(SP), R9
+ MOVL R9, 136(R8)
+
+ // ROUND2(35)
+ // SHUFFLE
+ MOVL 12(SP), R9
+ XORL (SP), R9
+ XORL 44(SP), R9
+ XORL 20(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 12(SP)
+
+ // FUNC2
+ MOVL R11, R15
+ XORL R12, R15
+ XORL R13, R15
+
+ // MIX
+ ROLL $+30, R11
+ ADDL R15, R14
+ MOVL R10, R8
+ ROLL $+5, R8
+ LEAL 1859775393(R14)(R9*1), R14
+ ADDL R8, R14
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 12(SP), R9
+ MOVL R9, 140(R8)
+
+ // ROUND2(36)
+ // SHUFFLE
+ MOVL 16(SP), R9
+ XORL 4(SP), R9
+ XORL 48(SP), R9
+ XORL 24(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 16(SP)
+
+ // FUNC2
+ MOVL R10, R15
+ XORL R11, R15
+ XORL R12, R15
+
+ // MIX
+ ROLL $+30, R10
+ ADDL R15, R13
+ MOVL R14, R8
+ ROLL $+5, R8
+ LEAL 1859775393(R13)(R9*1), R13
+ ADDL R8, R13
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 16(SP), R9
+ MOVL R9, 144(R8)
+
+ // ROUND2(37)
+ // SHUFFLE
+ MOVL 20(SP), R9
+ XORL 8(SP), R9
+ XORL 52(SP), R9
+ XORL 28(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 20(SP)
+
+ // FUNC2
+ MOVL R14, R15
+ XORL R10, R15
+ XORL R11, R15
+
+ // MIX
+ ROLL $+30, R14
+ ADDL R15, R12
+ MOVL R13, R8
+ ROLL $+5, R8
+ LEAL 1859775393(R12)(R9*1), R12
+ ADDL R8, R12
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 20(SP), R9
+ MOVL R9, 148(R8)
+
+ // ROUND2(38)
+ // SHUFFLE
+ MOVL 24(SP), R9
+ XORL 12(SP), R9
+ XORL 56(SP), R9
+ XORL 32(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 24(SP)
+
+ // FUNC2
+ MOVL R13, R15
+ XORL R14, R15
+ XORL R10, R15
+
+ // MIX
+ ROLL $+30, R13
+ ADDL R15, R11
+ MOVL R12, R8
+ ROLL $+5, R8
+ LEAL 1859775393(R11)(R9*1), R11
+ ADDL R8, R11
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 24(SP), R9
+ MOVL R9, 152(R8)
+
+ // ROUND2(39)
+ // SHUFFLE
+ MOVL 28(SP), R9
+ XORL 16(SP), R9
+ XORL 60(SP), R9
+ XORL 36(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 28(SP)
+
+ // FUNC2
+ MOVL R12, R15
+ XORL R13, R15
+ XORL R14, R15
+
+ // MIX
+ ROLL $+30, R12
+ ADDL R15, R10
+ MOVL R11, R8
+ ROLL $+5, R8
+ LEAL 1859775393(R10)(R9*1), R10
+ ADDL R8, R10
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 28(SP), R9
+ MOVL R9, 156(R8)
+
+ // ROUND3 (steps 40-59)
+ // ROUND3(40)
+ // SHUFFLE
+ MOVL 32(SP), R9
+ XORL 20(SP), R9
+ XORL (SP), R9
+ XORL 40(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 32(SP)
+
+ // FUNC3
+ MOVL R11, R8
+ ORL R12, R8
+ ANDL R13, R8
+ MOVL R11, R15
+ ANDL R12, R15
+ ORL R8, R15
+
+ // MIX
+ ROLL $+30, R11
+ ADDL R15, R14
+ MOVL R10, R8
+ ROLL $+5, R8
+ LEAL 2400959708(R14)(R9*1), R14
+ ADDL R8, R14
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 32(SP), R9
+ MOVL R9, 160(R8)
+
+ // ROUND3(41)
+ // SHUFFLE
+ MOVL 36(SP), R9
+ XORL 24(SP), R9
+ XORL 4(SP), R9
+ XORL 44(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 36(SP)
+
+ // FUNC3
+ MOVL R10, R8
+ ORL R11, R8
+ ANDL R12, R8
+ MOVL R10, R15
+ ANDL R11, R15
+ ORL R8, R15
+
+ // MIX
+ ROLL $+30, R10
+ ADDL R15, R13
+ MOVL R14, R8
+ ROLL $+5, R8
+ LEAL 2400959708(R13)(R9*1), R13
+ ADDL R8, R13
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 36(SP), R9
+ MOVL R9, 164(R8)
+
+ // ROUND3(42)
+ // SHUFFLE
+ MOVL 40(SP), R9
+ XORL 28(SP), R9
+ XORL 8(SP), R9
+ XORL 48(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 40(SP)
+
+ // FUNC3
+ MOVL R14, R8
+ ORL R10, R8
+ ANDL R11, R8
+ MOVL R14, R15
+ ANDL R10, R15
+ ORL R8, R15
+
+ // MIX
+ ROLL $+30, R14
+ ADDL R15, R12
+ MOVL R13, R8
+ ROLL $+5, R8
+ LEAL 2400959708(R12)(R9*1), R12
+ ADDL R8, R12
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 40(SP), R9
+ MOVL R9, 168(R8)
+
+ // ROUND3(43)
+ // SHUFFLE
+ MOVL 44(SP), R9
+ XORL 32(SP), R9
+ XORL 12(SP), R9
+ XORL 52(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 44(SP)
+
+ // FUNC3
+ MOVL R13, R8
+ ORL R14, R8
+ ANDL R10, R8
+ MOVL R13, R15
+ ANDL R14, R15
+ ORL R8, R15
+
+ // MIX
+ ROLL $+30, R13
+ ADDL R15, R11
+ MOVL R12, R8
+ ROLL $+5, R8
+ LEAL 2400959708(R11)(R9*1), R11
+ ADDL R8, R11
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 44(SP), R9
+ MOVL R9, 172(R8)
+
+ // ROUND3(44)
+ // SHUFFLE
+ MOVL 48(SP), R9
+ XORL 36(SP), R9
+ XORL 16(SP), R9
+ XORL 56(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 48(SP)
+
+ // FUNC3
+ MOVL R12, R8
+ ORL R13, R8
+ ANDL R14, R8
+ MOVL R12, R15
+ ANDL R13, R15
+ ORL R8, R15
+
+ // MIX
+ ROLL $+30, R12
+ ADDL R15, R10
+ MOVL R11, R8
+ ROLL $+5, R8
+ LEAL 2400959708(R10)(R9*1), R10
+ ADDL R8, R10
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 48(SP), R9
+ MOVL R9, 176(R8)
+
+ // ROUND3(45)
+ // SHUFFLE
+ MOVL 52(SP), R9
+ XORL 40(SP), R9
+ XORL 20(SP), R9
+ XORL 60(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 52(SP)
+
+ // FUNC3
+ MOVL R11, R8
+ ORL R12, R8
+ ANDL R13, R8
+ MOVL R11, R15
+ ANDL R12, R15
+ ORL R8, R15
+
+ // MIX
+ ROLL $+30, R11
+ ADDL R15, R14
+ MOVL R10, R8
+ ROLL $+5, R8
+ LEAL 2400959708(R14)(R9*1), R14
+ ADDL R8, R14
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 52(SP), R9
+ MOVL R9, 180(R8)
+
+ // ROUND3(46)
+ // SHUFFLE
+ MOVL 56(SP), R9
+ XORL 44(SP), R9
+ XORL 24(SP), R9
+ XORL (SP), R9
+ ROLL $+1, R9
+ MOVL R9, 56(SP)
+
+ // FUNC3
+ MOVL R10, R8
+ ORL R11, R8
+ ANDL R12, R8
+ MOVL R10, R15
+ ANDL R11, R15
+ ORL R8, R15
+
+ // MIX
+ ROLL $+30, R10
+ ADDL R15, R13
+ MOVL R14, R8
+ ROLL $+5, R8
+ LEAL 2400959708(R13)(R9*1), R13
+ ADDL R8, R13
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 56(SP), R9
+ MOVL R9, 184(R8)
+
+ // ROUND3(47)
+ // SHUFFLE
+ MOVL 60(SP), R9
+ XORL 48(SP), R9
+ XORL 28(SP), R9
+ XORL 4(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 60(SP)
+
+ // FUNC3
+ MOVL R14, R8
+ ORL R10, R8
+ ANDL R11, R8
+ MOVL R14, R15
+ ANDL R10, R15
+ ORL R8, R15
+
+ // MIX
+ ROLL $+30, R14
+ ADDL R15, R12
+ MOVL R13, R8
+ ROLL $+5, R8
+ LEAL 2400959708(R12)(R9*1), R12
+ ADDL R8, R12
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 60(SP), R9
+ MOVL R9, 188(R8)
+
+ // ROUND3(48)
+ // SHUFFLE
+ MOVL (SP), R9
+ XORL 52(SP), R9
+ XORL 32(SP), R9
+ XORL 8(SP), R9
+ ROLL $+1, R9
+ MOVL R9, (SP)
+
+ // FUNC3
+ MOVL R13, R8
+ ORL R14, R8
+ ANDL R10, R8
+ MOVL R13, R15
+ ANDL R14, R15
+ ORL R8, R15
+
+ // MIX
+ ROLL $+30, R13
+ ADDL R15, R11
+ MOVL R12, R8
+ ROLL $+5, R8
+ LEAL 2400959708(R11)(R9*1), R11
+ ADDL R8, R11
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL (SP), R9
+ MOVL R9, 192(R8)
+
+ // ROUND3(49)
+ // SHUFFLE
+ MOVL 4(SP), R9
+ XORL 56(SP), R9
+ XORL 36(SP), R9
+ XORL 12(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 4(SP)
+
+ // FUNC3
+ MOVL R12, R8
+ ORL R13, R8
+ ANDL R14, R8
+ MOVL R12, R15
+ ANDL R13, R15
+ ORL R8, R15
+
+ // MIX
+ ROLL $+30, R12
+ ADDL R15, R10
+ MOVL R11, R8
+ ROLL $+5, R8
+ LEAL 2400959708(R10)(R9*1), R10
+ ADDL R8, R10
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 4(SP), R9
+ MOVL R9, 196(R8)
+
+ // ROUND3(50)
+ // SHUFFLE
+ MOVL 8(SP), R9
+ XORL 60(SP), R9
+ XORL 40(SP), R9
+ XORL 16(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 8(SP)
+
+ // FUNC3
+ MOVL R11, R8
+ ORL R12, R8
+ ANDL R13, R8
+ MOVL R11, R15
+ ANDL R12, R15
+ ORL R8, R15
+
+ // MIX
+ ROLL $+30, R11
+ ADDL R15, R14
+ MOVL R10, R8
+ ROLL $+5, R8
+ LEAL 2400959708(R14)(R9*1), R14
+ ADDL R8, R14
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 8(SP), R9
+ MOVL R9, 200(R8)
+
+ // ROUND3(51)
+ // SHUFFLE
+ MOVL 12(SP), R9
+ XORL (SP), R9
+ XORL 44(SP), R9
+ XORL 20(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 12(SP)
+
+ // FUNC3
+ MOVL R10, R8
+ ORL R11, R8
+ ANDL R12, R8
+ MOVL R10, R15
+ ANDL R11, R15
+ ORL R8, R15
+
+ // MIX
+ ROLL $+30, R10
+ ADDL R15, R13
+ MOVL R14, R8
+ ROLL $+5, R8
+ LEAL 2400959708(R13)(R9*1), R13
+ ADDL R8, R13
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 12(SP), R9
+ MOVL R9, 204(R8)
+
+ // ROUND3(52)
+ // SHUFFLE
+ MOVL 16(SP), R9
+ XORL 4(SP), R9
+ XORL 48(SP), R9
+ XORL 24(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 16(SP)
+
+ // FUNC3
+ MOVL R14, R8
+ ORL R10, R8
+ ANDL R11, R8
+ MOVL R14, R15
+ ANDL R10, R15
+ ORL R8, R15
+
+ // MIX
+ ROLL $+30, R14
+ ADDL R15, R12
+ MOVL R13, R8
+ ROLL $+5, R8
+ LEAL 2400959708(R12)(R9*1), R12
+ ADDL R8, R12
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 16(SP), R9
+ MOVL R9, 208(R8)
+
+ // ROUND3(53)
+ // SHUFFLE
+ MOVL 20(SP), R9
+ XORL 8(SP), R9
+ XORL 52(SP), R9
+ XORL 28(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 20(SP)
+
+ // FUNC3
+ MOVL R13, R8
+ ORL R14, R8
+ ANDL R10, R8
+ MOVL R13, R15
+ ANDL R14, R15
+ ORL R8, R15
+
+ // MIX
+ ROLL $+30, R13
+ ADDL R15, R11
+ MOVL R12, R8
+ ROLL $+5, R8
+ LEAL 2400959708(R11)(R9*1), R11
+ ADDL R8, R11
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 20(SP), R9
+ MOVL R9, 212(R8)
+
+ // ROUND3(54)
+ // SHUFFLE
+ MOVL 24(SP), R9
+ XORL 12(SP), R9
+ XORL 56(SP), R9
+ XORL 32(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 24(SP)
+
+ // FUNC3
+ MOVL R12, R8
+ ORL R13, R8
+ ANDL R14, R8
+ MOVL R12, R15
+ ANDL R13, R15
+ ORL R8, R15
+
+ // MIX
+ ROLL $+30, R12
+ ADDL R15, R10
+ MOVL R11, R8
+ ROLL $+5, R8
+ LEAL 2400959708(R10)(R9*1), R10
+ ADDL R8, R10
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 24(SP), R9
+ MOVL R9, 216(R8)
+
+ // ROUND3(55)
+ // SHUFFLE
+ MOVL 28(SP), R9
+ XORL 16(SP), R9
+ XORL 60(SP), R9
+ XORL 36(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 28(SP)
+
+ // FUNC3
+ MOVL R11, R8
+ ORL R12, R8
+ ANDL R13, R8
+ MOVL R11, R15
+ ANDL R12, R15
+ ORL R8, R15
+
+ // MIX
+ ROLL $+30, R11
+ ADDL R15, R14
+ MOVL R10, R8
+ ROLL $+5, R8
+ LEAL 2400959708(R14)(R9*1), R14
+ ADDL R8, R14
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 28(SP), R9
+ MOVL R9, 220(R8)
+
+ // ROUND3(56)
+ // SHUFFLE
+ MOVL 32(SP), R9
+ XORL 20(SP), R9
+ XORL (SP), R9
+ XORL 40(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 32(SP)
+
+ // FUNC3
+ MOVL R10, R8
+ ORL R11, R8
+ ANDL R12, R8
+ MOVL R10, R15
+ ANDL R11, R15
+ ORL R8, R15
+
+ // MIX
+ ROLL $+30, R10
+ ADDL R15, R13
+ MOVL R14, R8
+ ROLL $+5, R8
+ LEAL 2400959708(R13)(R9*1), R13
+ ADDL R8, R13
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 32(SP), R9
+ MOVL R9, 224(R8)
+
+ // ROUND3(57)
+ // SHUFFLE
+ MOVL 36(SP), R9
+ XORL 24(SP), R9
+ XORL 4(SP), R9
+ XORL 44(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 36(SP)
+
+ // FUNC3
+ MOVL R14, R8
+ ORL R10, R8
+ ANDL R11, R8
+ MOVL R14, R15
+ ANDL R10, R15
+ ORL R8, R15
+
+ // MIX
+ ROLL $+30, R14
+ ADDL R15, R12
+ MOVL R13, R8
+ ROLL $+5, R8
+ LEAL 2400959708(R12)(R9*1), R12
+ ADDL R8, R12
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 36(SP), R9
+ MOVL R9, 228(R8)
+
+ // Load cs
+ MOVQ cs_base+56(FP), R8
+ MOVL R12, 20(R8)
+ MOVL R13, 24(R8)
+ MOVL R14, 28(R8)
+ MOVL R10, 32(R8)
+ MOVL R11, 36(R8)
+
+ // ROUND3(58)
+ // SHUFFLE
+ MOVL 40(SP), R9
+ XORL 28(SP), R9
+ XORL 8(SP), R9
+ XORL 48(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 40(SP)
+
+ // FUNC3
+ MOVL R13, R8
+ ORL R14, R8
+ ANDL R10, R8
+ MOVL R13, R15
+ ANDL R14, R15
+ ORL R8, R15
+
+ // MIX
+ ROLL $+30, R13
+ ADDL R15, R11
+ MOVL R12, R8
+ ROLL $+5, R8
+ LEAL 2400959708(R11)(R9*1), R11
+ ADDL R8, R11
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 40(SP), R9
+ MOVL R9, 232(R8)
+
+ // ROUND3(59)
+ // SHUFFLE
+ MOVL 44(SP), R9
+ XORL 32(SP), R9
+ XORL 12(SP), R9
+ XORL 52(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 44(SP)
+
+ // FUNC3
+ MOVL R12, R8
+ ORL R13, R8
+ ANDL R14, R8
+ MOVL R12, R15
+ ANDL R13, R15
+ ORL R8, R15
+
+ // MIX
+ ROLL $+30, R12
+ ADDL R15, R10
+ MOVL R11, R8
+ ROLL $+5, R8
+ LEAL 2400959708(R10)(R9*1), R10
+ ADDL R8, R10
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 44(SP), R9
+ MOVL R9, 236(R8)
+
+ // ROUND4 (steps 60-79)
+ // ROUND4(60)
+ // SHUFFLE
+ MOVL 48(SP), R9
+ XORL 36(SP), R9
+ XORL 16(SP), R9
+ XORL 56(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 48(SP)
+
+ // FUNC2
+ MOVL R11, R15
+ XORL R12, R15
+ XORL R13, R15
+
+ // MIX
+ ROLL $+30, R11
+ ADDL R15, R14
+ MOVL R10, R8
+ ROLL $+5, R8
+ LEAL 3395469782(R14)(R9*1), R14
+ ADDL R8, R14
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 48(SP), R9
+ MOVL R9, 240(R8)
+
+ // ROUND4(61)
+ // SHUFFLE
+ MOVL 52(SP), R9
+ XORL 40(SP), R9
+ XORL 20(SP), R9
+ XORL 60(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 52(SP)
+
+ // FUNC2
+ MOVL R10, R15
+ XORL R11, R15
+ XORL R12, R15
+
+ // MIX
+ ROLL $+30, R10
+ ADDL R15, R13
+ MOVL R14, R8
+ ROLL $+5, R8
+ LEAL 3395469782(R13)(R9*1), R13
+ ADDL R8, R13
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 52(SP), R9
+ MOVL R9, 244(R8)
+
+ // ROUND4(62)
+ // SHUFFLE
+ MOVL 56(SP), R9
+ XORL 44(SP), R9
+ XORL 24(SP), R9
+ XORL (SP), R9
+ ROLL $+1, R9
+ MOVL R9, 56(SP)
+
+ // FUNC2
+ MOVL R14, R15
+ XORL R10, R15
+ XORL R11, R15
+
+ // MIX
+ ROLL $+30, R14
+ ADDL R15, R12
+ MOVL R13, R8
+ ROLL $+5, R8
+ LEAL 3395469782(R12)(R9*1), R12
+ ADDL R8, R12
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 56(SP), R9
+ MOVL R9, 248(R8)
+
+ // ROUND4(63)
+ // SHUFFLE
+ MOVL 60(SP), R9
+ XORL 48(SP), R9
+ XORL 28(SP), R9
+ XORL 4(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 60(SP)
+
+ // FUNC2
+ MOVL R13, R15
+ XORL R14, R15
+ XORL R10, R15
+
+ // MIX
+ ROLL $+30, R13
+ ADDL R15, R11
+ MOVL R12, R8
+ ROLL $+5, R8
+ LEAL 3395469782(R11)(R9*1), R11
+ ADDL R8, R11
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 60(SP), R9
+ MOVL R9, 252(R8)
+
+ // ROUND4(64)
+ // SHUFFLE
+ MOVL (SP), R9
+ XORL 52(SP), R9
+ XORL 32(SP), R9
+ XORL 8(SP), R9
+ ROLL $+1, R9
+ MOVL R9, (SP)
+
+ // FUNC2
+ MOVL R12, R15
+ XORL R13, R15
+ XORL R14, R15
+
+ // MIX
+ ROLL $+30, R12
+ ADDL R15, R10
+ MOVL R11, R8
+ ROLL $+5, R8
+ LEAL 3395469782(R10)(R9*1), R10
+ ADDL R8, R10
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL (SP), R9
+ MOVL R9, 256(R8)
+
+ // Load cs
+ MOVQ cs_base+56(FP), R8
+ MOVL R10, 40(R8)
+ MOVL R11, 44(R8)
+ MOVL R12, 48(R8)
+ MOVL R13, 52(R8)
+ MOVL R14, 56(R8)
+
+ // ROUND4(65)
+ // SHUFFLE
+ MOVL 4(SP), R9
+ XORL 56(SP), R9
+ XORL 36(SP), R9
+ XORL 12(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 4(SP)
+
+ // FUNC2
+ MOVL R11, R15
+ XORL R12, R15
+ XORL R13, R15
+
+ // MIX
+ ROLL $+30, R11
+ ADDL R15, R14
+ MOVL R10, R8
+ ROLL $+5, R8
+ LEAL 3395469782(R14)(R9*1), R14
+ ADDL R8, R14
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 4(SP), R9
+ MOVL R9, 260(R8)
+
+ // ROUND4(66)
+ // SHUFFLE
+ MOVL 8(SP), R9
+ XORL 60(SP), R9
+ XORL 40(SP), R9
+ XORL 16(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 8(SP)
+
+ // FUNC2
+ MOVL R10, R15
+ XORL R11, R15
+ XORL R12, R15
+
+ // MIX
+ ROLL $+30, R10
+ ADDL R15, R13
+ MOVL R14, R8
+ ROLL $+5, R8
+ LEAL 3395469782(R13)(R9*1), R13
+ ADDL R8, R13
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 8(SP), R9
+ MOVL R9, 264(R8)
+
+ // ROUND4(67)
+ // SHUFFLE
+ MOVL 12(SP), R9
+ XORL (SP), R9
+ XORL 44(SP), R9
+ XORL 20(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 12(SP)
+
+ // FUNC2
+ MOVL R14, R15
+ XORL R10, R15
+ XORL R11, R15
+
+ // MIX
+ ROLL $+30, R14
+ ADDL R15, R12
+ MOVL R13, R8
+ ROLL $+5, R8
+ LEAL 3395469782(R12)(R9*1), R12
+ ADDL R8, R12
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 12(SP), R9
+ MOVL R9, 268(R8)
+
+ // ROUND4(68)
+ // SHUFFLE
+ MOVL 16(SP), R9
+ XORL 4(SP), R9
+ XORL 48(SP), R9
+ XORL 24(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 16(SP)
+
+ // FUNC2
+ MOVL R13, R15
+ XORL R14, R15
+ XORL R10, R15
+
+ // MIX
+ ROLL $+30, R13
+ ADDL R15, R11
+ MOVL R12, R8
+ ROLL $+5, R8
+ LEAL 3395469782(R11)(R9*1), R11
+ ADDL R8, R11
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 16(SP), R9
+ MOVL R9, 272(R8)
+
+ // ROUND4(69)
+ // SHUFFLE
+ MOVL 20(SP), R9
+ XORL 8(SP), R9
+ XORL 52(SP), R9
+ XORL 28(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 20(SP)
+
+ // FUNC2
+ MOVL R12, R15
+ XORL R13, R15
+ XORL R14, R15
+
+ // MIX
+ ROLL $+30, R12
+ ADDL R15, R10
+ MOVL R11, R8
+ ROLL $+5, R8
+ LEAL 3395469782(R10)(R9*1), R10
+ ADDL R8, R10
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 20(SP), R9
+ MOVL R9, 276(R8)
+
+ // ROUND4(70)
+ // SHUFFLE
+ MOVL 24(SP), R9
+ XORL 12(SP), R9
+ XORL 56(SP), R9
+ XORL 32(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 24(SP)
+
+ // FUNC2
+ MOVL R11, R15
+ XORL R12, R15
+ XORL R13, R15
+
+ // MIX
+ ROLL $+30, R11
+ ADDL R15, R14
+ MOVL R10, R8
+ ROLL $+5, R8
+ LEAL 3395469782(R14)(R9*1), R14
+ ADDL R8, R14
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 24(SP), R9
+ MOVL R9, 280(R8)
+
+ // ROUND4(71)
+ // SHUFFLE
+ MOVL 28(SP), R9
+ XORL 16(SP), R9
+ XORL 60(SP), R9
+ XORL 36(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 28(SP)
+
+ // FUNC2
+ MOVL R10, R15
+ XORL R11, R15
+ XORL R12, R15
+
+ // MIX
+ ROLL $+30, R10
+ ADDL R15, R13
+ MOVL R14, R8
+ ROLL $+5, R8
+ LEAL 3395469782(R13)(R9*1), R13
+ ADDL R8, R13
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 28(SP), R9
+ MOVL R9, 284(R8)
+
+ // ROUND4(72)
+ // SHUFFLE
+ MOVL 32(SP), R9
+ XORL 20(SP), R9
+ XORL (SP), R9
+ XORL 40(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 32(SP)
+
+ // FUNC2
+ MOVL R14, R15
+ XORL R10, R15
+ XORL R11, R15
+
+ // MIX
+ ROLL $+30, R14
+ ADDL R15, R12
+ MOVL R13, R8
+ ROLL $+5, R8
+ LEAL 3395469782(R12)(R9*1), R12
+ ADDL R8, R12
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 32(SP), R9
+ MOVL R9, 288(R8)
+
+ // ROUND4(73)
+ // SHUFFLE
+ MOVL 36(SP), R9
+ XORL 24(SP), R9
+ XORL 4(SP), R9
+ XORL 44(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 36(SP)
+
+ // FUNC2
+ MOVL R13, R15
+ XORL R14, R15
+ XORL R10, R15
+
+ // MIX
+ ROLL $+30, R13
+ ADDL R15, R11
+ MOVL R12, R8
+ ROLL $+5, R8
+ LEAL 3395469782(R11)(R9*1), R11
+ ADDL R8, R11
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 36(SP), R9
+ MOVL R9, 292(R8)
+
+ // ROUND4(74)
+ // SHUFFLE
+ MOVL 40(SP), R9
+ XORL 28(SP), R9
+ XORL 8(SP), R9
+ XORL 48(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 40(SP)
+
+ // FUNC2
+ MOVL R12, R15
+ XORL R13, R15
+ XORL R14, R15
+
+ // MIX
+ ROLL $+30, R12
+ ADDL R15, R10
+ MOVL R11, R8
+ ROLL $+5, R8
+ LEAL 3395469782(R10)(R9*1), R10
+ ADDL R8, R10
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 40(SP), R9
+ MOVL R9, 296(R8)
+
+ // ROUND4(75)
+ // SHUFFLE
+ MOVL 44(SP), R9
+ XORL 32(SP), R9
+ XORL 12(SP), R9
+ XORL 52(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 44(SP)
+
+ // FUNC2
+ MOVL R11, R15
+ XORL R12, R15
+ XORL R13, R15
+
+ // MIX
+ ROLL $+30, R11
+ ADDL R15, R14
+ MOVL R10, R8
+ ROLL $+5, R8
+ LEAL 3395469782(R14)(R9*1), R14
+ ADDL R8, R14
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 44(SP), R9
+ MOVL R9, 300(R8)
+
+ // ROUND4(76)
+ // SHUFFLE
+ MOVL 48(SP), R9
+ XORL 36(SP), R9
+ XORL 16(SP), R9
+ XORL 56(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 48(SP)
+
+ // FUNC2
+ MOVL R10, R15
+ XORL R11, R15
+ XORL R12, R15
+
+ // MIX
+ ROLL $+30, R10
+ ADDL R15, R13
+ MOVL R14, R8
+ ROLL $+5, R8
+ LEAL 3395469782(R13)(R9*1), R13
+ ADDL R8, R13
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 48(SP), R9
+ MOVL R9, 304(R8)
+
+ // ROUND4(77)
+ // SHUFFLE
+ MOVL 52(SP), R9
+ XORL 40(SP), R9
+ XORL 20(SP), R9
+ XORL 60(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 52(SP)
+
+ // FUNC2
+ MOVL R14, R15
+ XORL R10, R15
+ XORL R11, R15
+
+ // MIX
+ ROLL $+30, R14
+ ADDL R15, R12
+ MOVL R13, R8
+ ROLL $+5, R8
+ LEAL 3395469782(R12)(R9*1), R12
+ ADDL R8, R12
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 52(SP), R9
+ MOVL R9, 308(R8)
+
+ // ROUND4(78)
+ // SHUFFLE
+ MOVL 56(SP), R9
+ XORL 44(SP), R9
+ XORL 24(SP), R9
+ XORL (SP), R9
+ ROLL $+1, R9
+ MOVL R9, 56(SP)
+
+ // FUNC2
+ MOVL R13, R15
+ XORL R14, R15
+ XORL R10, R15
+
+ // MIX
+ ROLL $+30, R13
+ ADDL R15, R11
+ MOVL R12, R8
+ ROLL $+5, R8
+ LEAL 3395469782(R11)(R9*1), R11
+ ADDL R8, R11
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 56(SP), R9
+ MOVL R9, 312(R8)
+
+ // ROUND4(79)
+ // SHUFFLE
+ MOVL 60(SP), R9
+ XORL 48(SP), R9
+ XORL 28(SP), R9
+ XORL 4(SP), R9
+ ROLL $+1, R9
+ MOVL R9, 60(SP)
+
+ // FUNC2
+ MOVL R12, R15
+ XORL R13, R15
+ XORL R14, R15
+
+ // MIX
+ ROLL $+30, R12
+ ADDL R15, R10
+ MOVL R11, R8
+ ROLL $+5, R8
+ LEAL 3395469782(R10)(R9*1), R10
+ ADDL R8, R10
+
+ // Load m1
+ MOVQ m1_base+32(FP), R8
+ MOVL 60(SP), R9
+ MOVL R9, 316(R8)
+
+ // Add registers to temp hash.
+ ADDL R10, AX
+ ADDL R11, BX
+ ADDL R12, CX
+ ADDL R13, DX
+ ADDL R14, BP
+ ADDQ $+64, DI
+ CMPQ DI, SI
+ JB loop
+
+end:
+ MOVQ dig+0(FP), SI
+ MOVL AX, (SI)
+ MOVL BX, 4(SI)
+ MOVL CX, 8(SI)
+ MOVL DX, 12(SI)
+ MOVL BP, 16(SI)
+ RET
diff --git a/vendor/github.com/pjbgf/sha1cd/sha1cdblock_generic.go b/vendor/github.com/pjbgf/sha1cd/sha1cdblock_generic.go
new file mode 100644
index 00000000..ba8b96e8
--- /dev/null
+++ b/vendor/github.com/pjbgf/sha1cd/sha1cdblock_generic.go
@@ -0,0 +1,268 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Originally from: https://github.com/go/blob/master/src/crypto/sha1/sha1block.go
+// It has been modified to support collision detection.
+
+package sha1cd
+
+import (
+ "fmt"
+ "math/bits"
+
+ shared "github.com/pjbgf/sha1cd/internal"
+ "github.com/pjbgf/sha1cd/ubc"
+)
+
+// blockGeneric is a portable, pure Go version of the SHA-1 block step.
+// It's used by sha1block_generic.go and tests.
+func blockGeneric(dig *digest, p []byte) {
+ var w [16]uint32
+
+ // cs stores the pre-step compression state for only the steps required for the
+ // collision detection, which are 0, 58 and 65.
+ // Refer to ubc/const.go for more details.
+ cs := [shared.PreStepState][shared.WordBuffers]uint32{}
+
+ h0, h1, h2, h3, h4 := dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4]
+ for len(p) >= shared.Chunk {
+ m1 := [shared.Rounds]uint32{}
+ hi := 1
+
+ // Collision attacks are thwarted by hashing a detected near-collision block 3 times.
+ // Think of it as extending SHA-1 from 80-steps to 240-steps for such blocks:
+ // The best collision attacks against SHA-1 have complexity about 2^60,
+ // thus for 240-steps an immediate lower-bound for the best cryptanalytic attacks would be 2^180.
+ // An attacker would be better off using a generic birthday search of complexity 2^80.
+ rehash:
+ a, b, c, d, e := h0, h1, h2, h3, h4
+
+ // Each of the four 20-iteration rounds
+ // differs only in the computation of f and
+ // the choice of K (K0, K1, etc).
+ i := 0
+
+ // Store pre-step compression state for the collision detection.
+ cs[0] = [shared.WordBuffers]uint32{a, b, c, d, e}
+
+ for ; i < 16; i++ {
+ // load step
+ j := i * 4
+ w[i] = uint32(p[j])<<24 | uint32(p[j+1])<<16 | uint32(p[j+2])<<8 | uint32(p[j+3])
+
+ f := b&c | (^b)&d
+ t := bits.RotateLeft32(a, 5) + f + e + w[i&0xf] + shared.K0
+ a, b, c, d, e = t, a, bits.RotateLeft32(b, 30), c, d
+
+ // Store compression state for the collision detection.
+ m1[i] = w[i&0xf]
+ }
+ for ; i < 20; i++ {
+ tmp := w[(i-3)&0xf] ^ w[(i-8)&0xf] ^ w[(i-14)&0xf] ^ w[(i)&0xf]
+ w[i&0xf] = tmp<<1 | tmp>>(32-1)
+
+ f := b&c | (^b)&d
+ t := bits.RotateLeft32(a, 5) + f + e + w[i&0xf] + shared.K0
+ a, b, c, d, e = t, a, bits.RotateLeft32(b, 30), c, d
+
+ // Store compression state for the collision detection.
+ m1[i] = w[i&0xf]
+ }
+ for ; i < 40; i++ {
+ tmp := w[(i-3)&0xf] ^ w[(i-8)&0xf] ^ w[(i-14)&0xf] ^ w[(i)&0xf]
+ w[i&0xf] = tmp<<1 | tmp>>(32-1)
+
+ f := b ^ c ^ d
+ t := bits.RotateLeft32(a, 5) + f + e + w[i&0xf] + shared.K1
+ a, b, c, d, e = t, a, bits.RotateLeft32(b, 30), c, d
+
+ // Store compression state for the collision detection.
+ m1[i] = w[i&0xf]
+ }
+ for ; i < 60; i++ {
+ if i == 58 {
+ // Store pre-step compression state for the collision detection.
+ cs[1] = [shared.WordBuffers]uint32{a, b, c, d, e}
+ }
+
+ tmp := w[(i-3)&0xf] ^ w[(i-8)&0xf] ^ w[(i-14)&0xf] ^ w[(i)&0xf]
+ w[i&0xf] = tmp<<1 | tmp>>(32-1)
+
+ f := ((b | c) & d) | (b & c)
+ t := bits.RotateLeft32(a, 5) + f + e + w[i&0xf] + shared.K2
+ a, b, c, d, e = t, a, bits.RotateLeft32(b, 30), c, d
+
+ // Store compression state for the collision detection.
+ m1[i] = w[i&0xf]
+ }
+ for ; i < 80; i++ {
+ if i == 65 {
+ // Store pre-step compression state for the collision detection.
+ cs[2] = [shared.WordBuffers]uint32{a, b, c, d, e}
+ }
+
+ tmp := w[(i-3)&0xf] ^ w[(i-8)&0xf] ^ w[(i-14)&0xf] ^ w[(i)&0xf]
+ w[i&0xf] = tmp<<1 | tmp>>(32-1)
+
+ f := b ^ c ^ d
+ t := bits.RotateLeft32(a, 5) + f + e + w[i&0xf] + shared.K3
+ a, b, c, d, e = t, a, bits.RotateLeft32(b, 30), c, d
+
+ // Store compression state for the collision detection.
+ m1[i] = w[i&0xf]
+ }
+
+ h0 += a
+ h1 += b
+ h2 += c
+ h3 += d
+ h4 += e
+
+ if hi == 2 {
+ hi++
+ goto rehash
+ }
+
+ if hi == 1 {
+ col := checkCollision(m1, cs, [shared.WordBuffers]uint32{h0, h1, h2, h3, h4})
+ if col {
+ dig.col = true
+ hi++
+ goto rehash
+ }
+ }
+
+ p = p[shared.Chunk:]
+ }
+
+ dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4] = h0, h1, h2, h3, h4
+}
+
+func checkCollision(
+ m1 [shared.Rounds]uint32,
+ cs [shared.PreStepState][shared.WordBuffers]uint32,
+ state [shared.WordBuffers]uint32) bool {
+
+ if mask := ubc.CalculateDvMask(m1); mask != 0 {
+ dvs := ubc.SHA1_dvs()
+
+ for i := 0; dvs[i].DvType != 0; i++ {
+ if (mask & ((uint32)(1) << uint32(dvs[i].MaskB))) != 0 {
+ var csState [shared.WordBuffers]uint32
+ switch dvs[i].TestT {
+ case 58:
+ csState = cs[1]
+ case 65:
+ csState = cs[2]
+ case 0:
+ csState = cs[0]
+ default:
+ panic(fmt.Sprintf("dvs data is trying to use a testT that isn't available: %d", dvs[i].TestT))
+ }
+
+ col := hasCollided(
+ dvs[i].TestT, // testT is the step number
+ // m2 is a secondary message created XORing with
+ // ubc's DM prior to the SHA recompression step.
+ m1, dvs[i].Dm,
+ csState,
+ state)
+
+ if col {
+ return true
+ }
+ }
+ }
+ }
+ return false
+}
+
+func hasCollided(step uint32, m1, dm [shared.Rounds]uint32,
+ state [shared.WordBuffers]uint32, h [shared.WordBuffers]uint32) bool {
+ // Intermediary Hash Value.
+ ihv := [shared.WordBuffers]uint32{}
+
+ a, b, c, d, e := state[0], state[1], state[2], state[3], state[4]
+
+ // Walk backwards from current step to undo previous compression.
+ // The existing collision detection does not have dvs higher than 65,
+ // start value of i accordingly.
+ for i := uint32(64); i >= 60; i-- {
+ a, b, c, d, e = b, c, d, e, a
+ if step > i {
+ b = bits.RotateLeft32(b, -30)
+ f := b ^ c ^ d
+ e -= bits.RotateLeft32(a, 5) + f + shared.K3 + (m1[i] ^ dm[i]) // m2 = m1 ^ dm.
+ }
+ }
+ for i := uint32(59); i >= 40; i-- {
+ a, b, c, d, e = b, c, d, e, a
+ if step > i {
+ b = bits.RotateLeft32(b, -30)
+ f := ((b | c) & d) | (b & c)
+ e -= bits.RotateLeft32(a, 5) + f + shared.K2 + (m1[i] ^ dm[i])
+ }
+ }
+ for i := uint32(39); i >= 20; i-- {
+ a, b, c, d, e = b, c, d, e, a
+ if step > i {
+ b = bits.RotateLeft32(b, -30)
+ f := b ^ c ^ d
+ e -= bits.RotateLeft32(a, 5) + f + shared.K1 + (m1[i] ^ dm[i])
+ }
+ }
+ for i := uint32(20); i > 0; i-- {
+ j := i - 1
+ a, b, c, d, e = b, c, d, e, a
+ if step > j {
+ b = bits.RotateLeft32(b, -30) // undo the rotate left
+ f := b&c | (^b)&d
+ // subtract from e
+ e -= bits.RotateLeft32(a, 5) + f + shared.K0 + (m1[j] ^ dm[j])
+ }
+ }
+
+ ihv[0] = a
+ ihv[1] = b
+ ihv[2] = c
+ ihv[3] = d
+ ihv[4] = e
+ a = state[0]
+ b = state[1]
+ c = state[2]
+ d = state[3]
+ e = state[4]
+
+ // Recompress blocks based on the current step.
+ // The existing collision detection does not have dvs below 58, so they have been removed
+ // from the source code. If new dvs are added which target rounds below 40, that logic
+ // will need to be readded here.
+ for i := uint32(40); i < 60; i++ {
+ if step <= i {
+ f := ((b | c) & d) | (b & c)
+ t := bits.RotateLeft32(a, 5) + f + e + shared.K2 + (m1[i] ^ dm[i])
+ a, b, c, d, e = t, a, bits.RotateLeft32(b, 30), c, d
+ }
+ }
+ for i := uint32(60); i < 80; i++ {
+ if step <= i {
+ f := b ^ c ^ d
+ t := bits.RotateLeft32(a, 5) + f + e + shared.K3 + (m1[i] ^ dm[i])
+ a, b, c, d, e = t, a, bits.RotateLeft32(b, 30), c, d
+ }
+ }
+
+ ihv[0] += a
+ ihv[1] += b
+ ihv[2] += c
+ ihv[3] += d
+ ihv[4] += e
+
+ if ((ihv[0] ^ h[0]) | (ihv[1] ^ h[1]) |
+ (ihv[2] ^ h[2]) | (ihv[3] ^ h[3]) | (ihv[4] ^ h[4])) == 0 {
+ return true
+ }
+
+ return false
+}
diff --git a/vendor/github.com/pjbgf/sha1cd/sha1cdblock_noasm.go b/vendor/github.com/pjbgf/sha1cd/sha1cdblock_noasm.go
new file mode 100644
index 00000000..15bae5a7
--- /dev/null
+++ b/vendor/github.com/pjbgf/sha1cd/sha1cdblock_noasm.go
@@ -0,0 +1,8 @@
+//go:build !amd64 || noasm || !gc
+// +build !amd64 noasm !gc
+
+package sha1cd
+
+func block(dig *digest, p []byte) {
+ blockGeneric(dig, p)
+}
diff --git a/vendor/github.com/pjbgf/sha1cd/ubc/check.go b/vendor/github.com/pjbgf/sha1cd/ubc/check.go
new file mode 100644
index 00000000..167a5558
--- /dev/null
+++ b/vendor/github.com/pjbgf/sha1cd/ubc/check.go
@@ -0,0 +1,368 @@
+// Based on the C implementation from Marc Stevens and Dan Shumow.
+// https://github.com/cr-marcstevens/sha1collisiondetection
+
+package ubc
+
+type DvInfo struct {
+ // DvType, DvK and DvB define the DV: I(K,B) or II(K,B) (see the paper).
+ // https://marc-stevens.nl/research/papers/C13-S.pdf
+ DvType uint32
+ DvK uint32
+ DvB uint32
+
+ // TestT is the step to do the recompression from for collision detection.
+ TestT uint32
+
+ // MaskI and MaskB define the bit to check for each DV in the dvmask returned by ubc_check.
+ MaskI uint32
+ MaskB uint32
+
+ // Dm is the expanded message block XOR-difference defined by the DV.
+ Dm [80]uint32
+}
+
+// CalculateDvMask takes as input an expanded message block and verifies the unavoidable bitconditions
+// for all listed DVs. It returns a dvmask where each bit belonging to a DV is set if all
+// unavoidable bitconditions for that DV have been met.
+// Thus, one needs to do the recompression check for each DV that has its bit set.
+func CalculateDvMask(W [80]uint32) uint32 {
+ mask := uint32(0xFFFFFFFF)
+ mask &= (((((W[44] ^ W[45]) >> 29) & 1) - 1) | ^(DV_I_48_0_bit | DV_I_51_0_bit | DV_I_52_0_bit | DV_II_45_0_bit | DV_II_46_0_bit | DV_II_50_0_bit | DV_II_51_0_bit))
+ mask &= (((((W[49] ^ W[50]) >> 29) & 1) - 1) | ^(DV_I_46_0_bit | DV_II_45_0_bit | DV_II_50_0_bit | DV_II_51_0_bit | DV_II_55_0_bit | DV_II_56_0_bit))
+ mask &= (((((W[48] ^ W[49]) >> 29) & 1) - 1) | ^(DV_I_45_0_bit | DV_I_52_0_bit | DV_II_49_0_bit | DV_II_50_0_bit | DV_II_54_0_bit | DV_II_55_0_bit))
+ mask &= ((((W[47] ^ (W[50] >> 25)) & (1 << 4)) - (1 << 4)) | ^(DV_I_47_0_bit | DV_I_49_0_bit | DV_I_51_0_bit | DV_II_45_0_bit | DV_II_51_0_bit | DV_II_56_0_bit))
+ mask &= (((((W[47] ^ W[48]) >> 29) & 1) - 1) | ^(DV_I_44_0_bit | DV_I_51_0_bit | DV_II_48_0_bit | DV_II_49_0_bit | DV_II_53_0_bit | DV_II_54_0_bit))
+ mask &= (((((W[46] >> 4) ^ (W[49] >> 29)) & 1) - 1) | ^(DV_I_46_0_bit | DV_I_48_0_bit | DV_I_50_0_bit | DV_I_52_0_bit | DV_II_50_0_bit | DV_II_55_0_bit))
+ mask &= (((((W[46] ^ W[47]) >> 29) & 1) - 1) | ^(DV_I_43_0_bit | DV_I_50_0_bit | DV_II_47_0_bit | DV_II_48_0_bit | DV_II_52_0_bit | DV_II_53_0_bit))
+ mask &= (((((W[45] >> 4) ^ (W[48] >> 29)) & 1) - 1) | ^(DV_I_45_0_bit | DV_I_47_0_bit | DV_I_49_0_bit | DV_I_51_0_bit | DV_II_49_0_bit | DV_II_54_0_bit))
+ mask &= (((((W[45] ^ W[46]) >> 29) & 1) - 1) | ^(DV_I_49_0_bit | DV_I_52_0_bit | DV_II_46_0_bit | DV_II_47_0_bit | DV_II_51_0_bit | DV_II_52_0_bit))
+ mask &= (((((W[44] >> 4) ^ (W[47] >> 29)) & 1) - 1) | ^(DV_I_44_0_bit | DV_I_46_0_bit | DV_I_48_0_bit | DV_I_50_0_bit | DV_II_48_0_bit | DV_II_53_0_bit))
+ mask &= (((((W[43] >> 4) ^ (W[46] >> 29)) & 1) - 1) | ^(DV_I_43_0_bit | DV_I_45_0_bit | DV_I_47_0_bit | DV_I_49_0_bit | DV_II_47_0_bit | DV_II_52_0_bit))
+ mask &= (((((W[43] ^ W[44]) >> 29) & 1) - 1) | ^(DV_I_47_0_bit | DV_I_50_0_bit | DV_I_51_0_bit | DV_II_45_0_bit | DV_II_49_0_bit | DV_II_50_0_bit))
+ mask &= (((((W[42] >> 4) ^ (W[45] >> 29)) & 1) - 1) | ^(DV_I_44_0_bit | DV_I_46_0_bit | DV_I_48_0_bit | DV_I_52_0_bit | DV_II_46_0_bit | DV_II_51_0_bit))
+ mask &= (((((W[41] >> 4) ^ (W[44] >> 29)) & 1) - 1) | ^(DV_I_43_0_bit | DV_I_45_0_bit | DV_I_47_0_bit | DV_I_51_0_bit | DV_II_45_0_bit | DV_II_50_0_bit))
+ mask &= (((((W[40] ^ W[41]) >> 29) & 1) - 1) | ^(DV_I_44_0_bit | DV_I_47_0_bit | DV_I_48_0_bit | DV_II_46_0_bit | DV_II_47_0_bit | DV_II_56_0_bit))
+ mask &= (((((W[54] ^ W[55]) >> 29) & 1) - 1) | ^(DV_I_51_0_bit | DV_II_47_0_bit | DV_II_50_0_bit | DV_II_55_0_bit | DV_II_56_0_bit))
+ mask &= (((((W[53] ^ W[54]) >> 29) & 1) - 1) | ^(DV_I_50_0_bit | DV_II_46_0_bit | DV_II_49_0_bit | DV_II_54_0_bit | DV_II_55_0_bit))
+ mask &= (((((W[52] ^ W[53]) >> 29) & 1) - 1) | ^(DV_I_49_0_bit | DV_II_45_0_bit | DV_II_48_0_bit | DV_II_53_0_bit | DV_II_54_0_bit))
+ mask &= ((((W[50] ^ (W[53] >> 25)) & (1 << 4)) - (1 << 4)) | ^(DV_I_50_0_bit | DV_I_52_0_bit | DV_II_46_0_bit | DV_II_48_0_bit | DV_II_54_0_bit))
+ mask &= (((((W[50] ^ W[51]) >> 29) & 1) - 1) | ^(DV_I_47_0_bit | DV_II_46_0_bit | DV_II_51_0_bit | DV_II_52_0_bit | DV_II_56_0_bit))
+ mask &= ((((W[49] ^ (W[52] >> 25)) & (1 << 4)) - (1 << 4)) | ^(DV_I_49_0_bit | DV_I_51_0_bit | DV_II_45_0_bit | DV_II_47_0_bit | DV_II_53_0_bit))
+ mask &= ((((W[48] ^ (W[51] >> 25)) & (1 << 4)) - (1 << 4)) | ^(DV_I_48_0_bit | DV_I_50_0_bit | DV_I_52_0_bit | DV_II_46_0_bit | DV_II_52_0_bit))
+ mask &= (((((W[42] ^ W[43]) >> 29) & 1) - 1) | ^(DV_I_46_0_bit | DV_I_49_0_bit | DV_I_50_0_bit | DV_II_48_0_bit | DV_II_49_0_bit))
+ mask &= (((((W[41] ^ W[42]) >> 29) & 1) - 1) | ^(DV_I_45_0_bit | DV_I_48_0_bit | DV_I_49_0_bit | DV_II_47_0_bit | DV_II_48_0_bit))
+ mask &= (((((W[40] >> 4) ^ (W[43] >> 29)) & 1) - 1) | ^(DV_I_44_0_bit | DV_I_46_0_bit | DV_I_50_0_bit | DV_II_49_0_bit | DV_II_56_0_bit))
+ mask &= (((((W[39] >> 4) ^ (W[42] >> 29)) & 1) - 1) | ^(DV_I_43_0_bit | DV_I_45_0_bit | DV_I_49_0_bit | DV_II_48_0_bit | DV_II_55_0_bit))
+
+ if (mask & (DV_I_44_0_bit | DV_I_48_0_bit | DV_II_47_0_bit | DV_II_54_0_bit | DV_II_56_0_bit)) != 0 {
+ mask &= (((((W[38] >> 4) ^ (W[41] >> 29)) & 1) - 1) | ^(DV_I_44_0_bit | DV_I_48_0_bit | DV_II_47_0_bit | DV_II_54_0_bit | DV_II_56_0_bit))
+ }
+ mask &= (((((W[37] >> 4) ^ (W[40] >> 29)) & 1) - 1) | ^(DV_I_43_0_bit | DV_I_47_0_bit | DV_II_46_0_bit | DV_II_53_0_bit | DV_II_55_0_bit))
+ if (mask & (DV_I_52_0_bit | DV_II_48_0_bit | DV_II_51_0_bit | DV_II_56_0_bit)) != 0 {
+ mask &= (((((W[55] ^ W[56]) >> 29) & 1) - 1) | ^(DV_I_52_0_bit | DV_II_48_0_bit | DV_II_51_0_bit | DV_II_56_0_bit))
+ }
+ if (mask & (DV_I_52_0_bit | DV_II_48_0_bit | DV_II_50_0_bit | DV_II_56_0_bit)) != 0 {
+ mask &= ((((W[52] ^ (W[55] >> 25)) & (1 << 4)) - (1 << 4)) | ^(DV_I_52_0_bit | DV_II_48_0_bit | DV_II_50_0_bit | DV_II_56_0_bit))
+ }
+ if (mask & (DV_I_51_0_bit | DV_II_47_0_bit | DV_II_49_0_bit | DV_II_55_0_bit)) != 0 {
+ mask &= ((((W[51] ^ (W[54] >> 25)) & (1 << 4)) - (1 << 4)) | ^(DV_I_51_0_bit | DV_II_47_0_bit | DV_II_49_0_bit | DV_II_55_0_bit))
+ }
+ if (mask & (DV_I_48_0_bit | DV_II_47_0_bit | DV_II_52_0_bit | DV_II_53_0_bit)) != 0 {
+ mask &= (((((W[51] ^ W[52]) >> 29) & 1) - 1) | ^(DV_I_48_0_bit | DV_II_47_0_bit | DV_II_52_0_bit | DV_II_53_0_bit))
+ }
+ if (mask & (DV_I_46_0_bit | DV_I_49_0_bit | DV_II_45_0_bit | DV_II_48_0_bit)) != 0 {
+ mask &= (((((W[36] >> 4) ^ (W[40] >> 29)) & 1) - 1) | ^(DV_I_46_0_bit | DV_I_49_0_bit | DV_II_45_0_bit | DV_II_48_0_bit))
+ }
+ if (mask & (DV_I_52_0_bit | DV_II_48_0_bit | DV_II_49_0_bit)) != 0 {
+ mask &= ((0 - (((W[53] ^ W[56]) >> 29) & 1)) | ^(DV_I_52_0_bit | DV_II_48_0_bit | DV_II_49_0_bit))
+ }
+ if (mask & (DV_I_50_0_bit | DV_II_46_0_bit | DV_II_47_0_bit)) != 0 {
+ mask &= ((0 - (((W[51] ^ W[54]) >> 29) & 1)) | ^(DV_I_50_0_bit | DV_II_46_0_bit | DV_II_47_0_bit))
+ }
+ if (mask & (DV_I_49_0_bit | DV_I_51_0_bit | DV_II_45_0_bit)) != 0 {
+ mask &= ((0 - (((W[50] ^ W[52]) >> 29) & 1)) | ^(DV_I_49_0_bit | DV_I_51_0_bit | DV_II_45_0_bit))
+ }
+ if (mask & (DV_I_48_0_bit | DV_I_50_0_bit | DV_I_52_0_bit)) != 0 {
+ mask &= ((0 - (((W[49] ^ W[51]) >> 29) & 1)) | ^(DV_I_48_0_bit | DV_I_50_0_bit | DV_I_52_0_bit))
+ }
+ if (mask & (DV_I_47_0_bit | DV_I_49_0_bit | DV_I_51_0_bit)) != 0 {
+ mask &= ((0 - (((W[48] ^ W[50]) >> 29) & 1)) | ^(DV_I_47_0_bit | DV_I_49_0_bit | DV_I_51_0_bit))
+ }
+ if (mask & (DV_I_46_0_bit | DV_I_48_0_bit | DV_I_50_0_bit)) != 0 {
+ mask &= ((0 - (((W[47] ^ W[49]) >> 29) & 1)) | ^(DV_I_46_0_bit | DV_I_48_0_bit | DV_I_50_0_bit))
+ }
+ if (mask & (DV_I_45_0_bit | DV_I_47_0_bit | DV_I_49_0_bit)) != 0 {
+ mask &= ((0 - (((W[46] ^ W[48]) >> 29) & 1)) | ^(DV_I_45_0_bit | DV_I_47_0_bit | DV_I_49_0_bit))
+ }
+ mask &= ((((W[45] ^ W[47]) & (1 << 6)) - (1 << 6)) | ^(DV_I_47_2_bit | DV_I_49_2_bit | DV_I_51_2_bit))
+ if (mask & (DV_I_44_0_bit | DV_I_46_0_bit | DV_I_48_0_bit)) != 0 {
+ mask &= ((0 - (((W[45] ^ W[47]) >> 29) & 1)) | ^(DV_I_44_0_bit | DV_I_46_0_bit | DV_I_48_0_bit))
+ }
+ mask &= (((((W[44] ^ W[46]) >> 6) & 1) - 1) | ^(DV_I_46_2_bit | DV_I_48_2_bit | DV_I_50_2_bit))
+ if (mask & (DV_I_43_0_bit | DV_I_45_0_bit | DV_I_47_0_bit)) != 0 {
+ mask &= ((0 - (((W[44] ^ W[46]) >> 29) & 1)) | ^(DV_I_43_0_bit | DV_I_45_0_bit | DV_I_47_0_bit))
+ }
+ mask &= ((0 - ((W[41] ^ (W[42] >> 5)) & (1 << 1))) | ^(DV_I_48_2_bit | DV_II_46_2_bit | DV_II_51_2_bit))
+ mask &= ((0 - ((W[40] ^ (W[41] >> 5)) & (1 << 1))) | ^(DV_I_47_2_bit | DV_I_51_2_bit | DV_II_50_2_bit))
+ if (mask & (DV_I_44_0_bit | DV_I_46_0_bit | DV_II_56_0_bit)) != 0 {
+ mask &= ((0 - (((W[40] ^ W[42]) >> 4) & 1)) | ^(DV_I_44_0_bit | DV_I_46_0_bit | DV_II_56_0_bit))
+ }
+ mask &= ((0 - ((W[39] ^ (W[40] >> 5)) & (1 << 1))) | ^(DV_I_46_2_bit | DV_I_50_2_bit | DV_II_49_2_bit))
+ if (mask & (DV_I_43_0_bit | DV_I_45_0_bit | DV_II_55_0_bit)) != 0 {
+ mask &= ((0 - (((W[39] ^ W[41]) >> 4) & 1)) | ^(DV_I_43_0_bit | DV_I_45_0_bit | DV_II_55_0_bit))
+ }
+ if (mask & (DV_I_44_0_bit | DV_II_54_0_bit | DV_II_56_0_bit)) != 0 {
+ mask &= ((0 - (((W[38] ^ W[40]) >> 4) & 1)) | ^(DV_I_44_0_bit | DV_II_54_0_bit | DV_II_56_0_bit))
+ }
+ if (mask & (DV_I_43_0_bit | DV_II_53_0_bit | DV_II_55_0_bit)) != 0 {
+ mask &= ((0 - (((W[37] ^ W[39]) >> 4) & 1)) | ^(DV_I_43_0_bit | DV_II_53_0_bit | DV_II_55_0_bit))
+ }
+ mask &= ((0 - ((W[36] ^ (W[37] >> 5)) & (1 << 1))) | ^(DV_I_47_2_bit | DV_I_50_2_bit | DV_II_46_2_bit))
+ if (mask & (DV_I_45_0_bit | DV_I_48_0_bit | DV_II_47_0_bit)) != 0 {
+ mask &= (((((W[35] >> 4) ^ (W[39] >> 29)) & 1) - 1) | ^(DV_I_45_0_bit | DV_I_48_0_bit | DV_II_47_0_bit))
+ }
+ if (mask & (DV_I_48_0_bit | DV_II_48_0_bit)) != 0 {
+ mask &= ((0 - ((W[63] ^ (W[64] >> 5)) & (1 << 0))) | ^(DV_I_48_0_bit | DV_II_48_0_bit))
+ }
+ if (mask & (DV_I_45_0_bit | DV_II_45_0_bit)) != 0 {
+ mask &= ((0 - ((W[63] ^ (W[64] >> 5)) & (1 << 1))) | ^(DV_I_45_0_bit | DV_II_45_0_bit))
+ }
+ if (mask & (DV_I_47_0_bit | DV_II_47_0_bit)) != 0 {
+ mask &= ((0 - ((W[62] ^ (W[63] >> 5)) & (1 << 0))) | ^(DV_I_47_0_bit | DV_II_47_0_bit))
+ }
+ if (mask & (DV_I_46_0_bit | DV_II_46_0_bit)) != 0 {
+ mask &= ((0 - ((W[61] ^ (W[62] >> 5)) & (1 << 0))) | ^(DV_I_46_0_bit | DV_II_46_0_bit))
+ }
+ mask &= ((0 - ((W[61] ^ (W[62] >> 5)) & (1 << 2))) | ^(DV_I_46_2_bit | DV_II_46_2_bit))
+ if (mask & (DV_I_45_0_bit | DV_II_45_0_bit)) != 0 {
+ mask &= ((0 - ((W[60] ^ (W[61] >> 5)) & (1 << 0))) | ^(DV_I_45_0_bit | DV_II_45_0_bit))
+ }
+ if (mask & (DV_II_51_0_bit | DV_II_54_0_bit)) != 0 {
+ mask &= (((((W[58] ^ W[59]) >> 29) & 1) - 1) | ^(DV_II_51_0_bit | DV_II_54_0_bit))
+ }
+ if (mask & (DV_II_50_0_bit | DV_II_53_0_bit)) != 0 {
+ mask &= (((((W[57] ^ W[58]) >> 29) & 1) - 1) | ^(DV_II_50_0_bit | DV_II_53_0_bit))
+ }
+ if (mask & (DV_II_52_0_bit | DV_II_54_0_bit)) != 0 {
+ mask &= ((((W[56] ^ (W[59] >> 25)) & (1 << 4)) - (1 << 4)) | ^(DV_II_52_0_bit | DV_II_54_0_bit))
+ }
+ if (mask & (DV_II_51_0_bit | DV_II_52_0_bit)) != 0 {
+ mask &= ((0 - (((W[56] ^ W[59]) >> 29) & 1)) | ^(DV_II_51_0_bit | DV_II_52_0_bit))
+ }
+ if (mask & (DV_II_49_0_bit | DV_II_52_0_bit)) != 0 {
+ mask &= (((((W[56] ^ W[57]) >> 29) & 1) - 1) | ^(DV_II_49_0_bit | DV_II_52_0_bit))
+ }
+ if (mask & (DV_II_51_0_bit | DV_II_53_0_bit)) != 0 {
+ mask &= ((((W[55] ^ (W[58] >> 25)) & (1 << 4)) - (1 << 4)) | ^(DV_II_51_0_bit | DV_II_53_0_bit))
+ }
+ if (mask & (DV_II_50_0_bit | DV_II_52_0_bit)) != 0 {
+ mask &= ((((W[54] ^ (W[57] >> 25)) & (1 << 4)) - (1 << 4)) | ^(DV_II_50_0_bit | DV_II_52_0_bit))
+ }
+ if (mask & (DV_II_49_0_bit | DV_II_51_0_bit)) != 0 {
+ mask &= ((((W[53] ^ (W[56] >> 25)) & (1 << 4)) - (1 << 4)) | ^(DV_II_49_0_bit | DV_II_51_0_bit))
+ }
+ mask &= ((((W[51] ^ (W[50] >> 5)) & (1 << 1)) - (1 << 1)) | ^(DV_I_50_2_bit | DV_II_46_2_bit))
+ mask &= ((((W[48] ^ W[50]) & (1 << 6)) - (1 << 6)) | ^(DV_I_50_2_bit | DV_II_46_2_bit))
+ if (mask & (DV_I_51_0_bit | DV_I_52_0_bit)) != 0 {
+ mask &= ((0 - (((W[48] ^ W[55]) >> 29) & 1)) | ^(DV_I_51_0_bit | DV_I_52_0_bit))
+ }
+ mask &= ((((W[47] ^ W[49]) & (1 << 6)) - (1 << 6)) | ^(DV_I_49_2_bit | DV_I_51_2_bit))
+ mask &= ((((W[48] ^ (W[47] >> 5)) & (1 << 1)) - (1 << 1)) | ^(DV_I_47_2_bit | DV_II_51_2_bit))
+ mask &= ((((W[46] ^ W[48]) & (1 << 6)) - (1 << 6)) | ^(DV_I_48_2_bit | DV_I_50_2_bit))
+ mask &= ((((W[47] ^ (W[46] >> 5)) & (1 << 1)) - (1 << 1)) | ^(DV_I_46_2_bit | DV_II_50_2_bit))
+ mask &= ((0 - ((W[44] ^ (W[45] >> 5)) & (1 << 1))) | ^(DV_I_51_2_bit | DV_II_49_2_bit))
+ mask &= ((((W[43] ^ W[45]) & (1 << 6)) - (1 << 6)) | ^(DV_I_47_2_bit | DV_I_49_2_bit))
+ mask &= (((((W[42] ^ W[44]) >> 6) & 1) - 1) | ^(DV_I_46_2_bit | DV_I_48_2_bit))
+ mask &= ((((W[43] ^ (W[42] >> 5)) & (1 << 1)) - (1 << 1)) | ^(DV_II_46_2_bit | DV_II_51_2_bit))
+ mask &= ((((W[42] ^ (W[41] >> 5)) & (1 << 1)) - (1 << 1)) | ^(DV_I_51_2_bit | DV_II_50_2_bit))
+ mask &= ((((W[41] ^ (W[40] >> 5)) & (1 << 1)) - (1 << 1)) | ^(DV_I_50_2_bit | DV_II_49_2_bit))
+ if (mask & (DV_I_52_0_bit | DV_II_51_0_bit)) != 0 {
+ mask &= ((((W[39] ^ (W[43] >> 25)) & (1 << 4)) - (1 << 4)) | ^(DV_I_52_0_bit | DV_II_51_0_bit))
+ }
+ if (mask & (DV_I_51_0_bit | DV_II_50_0_bit)) != 0 {
+ mask &= ((((W[38] ^ (W[42] >> 25)) & (1 << 4)) - (1 << 4)) | ^(DV_I_51_0_bit | DV_II_50_0_bit))
+ }
+ if (mask & (DV_I_48_2_bit | DV_I_51_2_bit)) != 0 {
+ mask &= ((0 - ((W[37] ^ (W[38] >> 5)) & (1 << 1))) | ^(DV_I_48_2_bit | DV_I_51_2_bit))
+ }
+ if (mask & (DV_I_50_0_bit | DV_II_49_0_bit)) != 0 {
+ mask &= ((((W[37] ^ (W[41] >> 25)) & (1 << 4)) - (1 << 4)) | ^(DV_I_50_0_bit | DV_II_49_0_bit))
+ }
+ if (mask & (DV_II_52_0_bit | DV_II_54_0_bit)) != 0 {
+ mask &= ((0 - ((W[36] ^ W[38]) & (1 << 4))) | ^(DV_II_52_0_bit | DV_II_54_0_bit))
+ }
+ mask &= ((0 - ((W[35] ^ (W[36] >> 5)) & (1 << 1))) | ^(DV_I_46_2_bit | DV_I_49_2_bit))
+ if (mask & (DV_I_51_0_bit | DV_II_47_0_bit)) != 0 {
+ mask &= ((((W[35] ^ (W[39] >> 25)) & (1 << 3)) - (1 << 3)) | ^(DV_I_51_0_bit | DV_II_47_0_bit))
+ }
+
+ if mask != 0 {
+ if (mask & DV_I_43_0_bit) != 0 {
+ if not((W[61]^(W[62]>>5))&(1<<1)) != 0 ||
+ not(not((W[59]^(W[63]>>25))&(1<<5))) != 0 ||
+ not((W[58]^(W[63]>>30))&(1<<0)) != 0 {
+ mask &= ^DV_I_43_0_bit
+ }
+ }
+ if (mask & DV_I_44_0_bit) != 0 {
+ if not((W[62]^(W[63]>>5))&(1<<1)) != 0 ||
+ not(not((W[60]^(W[64]>>25))&(1<<5))) != 0 ||
+ not((W[59]^(W[64]>>30))&(1<<0)) != 0 {
+ mask &= ^DV_I_44_0_bit
+ }
+ }
+ if (mask & DV_I_46_2_bit) != 0 {
+ mask &= ((^((W[40] ^ W[42]) >> 2)) | ^DV_I_46_2_bit)
+ }
+ if (mask & DV_I_47_2_bit) != 0 {
+ if not((W[62]^(W[63]>>5))&(1<<2)) != 0 ||
+ not(not((W[41]^W[43])&(1<<6))) != 0 {
+ mask &= ^DV_I_47_2_bit
+ }
+ }
+ if (mask & DV_I_48_2_bit) != 0 {
+ if not((W[63]^(W[64]>>5))&(1<<2)) != 0 ||
+ not(not((W[48]^(W[49]<<5))&(1<<6))) != 0 {
+ mask &= ^DV_I_48_2_bit
+ }
+ }
+ if (mask & DV_I_49_2_bit) != 0 {
+ if not(not((W[49]^(W[50]<<5))&(1<<6))) != 0 ||
+ not((W[42]^W[50])&(1<<1)) != 0 ||
+ not(not((W[39]^(W[40]<<5))&(1<<6))) != 0 ||
+ not((W[38]^W[40])&(1<<1)) != 0 {
+ mask &= ^DV_I_49_2_bit
+ }
+ }
+ if (mask & DV_I_50_0_bit) != 0 {
+ mask &= (((W[36] ^ W[37]) << 7) | ^DV_I_50_0_bit)
+ }
+ if (mask & DV_I_50_2_bit) != 0 {
+ mask &= (((W[43] ^ W[51]) << 11) | ^DV_I_50_2_bit)
+ }
+ if (mask & DV_I_51_0_bit) != 0 {
+ mask &= (((W[37] ^ W[38]) << 9) | ^DV_I_51_0_bit)
+ }
+ if (mask & DV_I_51_2_bit) != 0 {
+ if not(not((W[51]^(W[52]<<5))&(1<<6))) != 0 ||
+ not(not((W[49]^W[51])&(1<<6))) != 0 ||
+ not(not((W[37]^(W[37]>>5))&(1<<1))) != 0 ||
+ not(not((W[35]^(W[39]>>25))&(1<<5))) != 0 {
+ mask &= ^DV_I_51_2_bit
+ }
+ }
+ if (mask & DV_I_52_0_bit) != 0 {
+ mask &= (((W[38] ^ W[39]) << 11) | ^DV_I_52_0_bit)
+ }
+ if (mask & DV_II_46_2_bit) != 0 {
+ mask &= (((W[47] ^ W[51]) << 17) | ^DV_II_46_2_bit)
+ }
+ if (mask & DV_II_48_0_bit) != 0 {
+ if not(not((W[36]^(W[40]>>25))&(1<<3))) != 0 ||
+ not((W[35]^(W[40]<<2))&(1<<30)) != 0 {
+ mask &= ^DV_II_48_0_bit
+ }
+ }
+ if (mask & DV_II_49_0_bit) != 0 {
+ if not(not((W[37]^(W[41]>>25))&(1<<3))) != 0 ||
+ not((W[36]^(W[41]<<2))&(1<<30)) != 0 {
+ mask &= ^DV_II_49_0_bit
+ }
+ }
+ if (mask & DV_II_49_2_bit) != 0 {
+ if not(not((W[53]^(W[54]<<5))&(1<<6))) != 0 ||
+ not(not((W[51]^W[53])&(1<<6))) != 0 ||
+ not((W[50]^W[54])&(1<<1)) != 0 ||
+ not(not((W[45]^(W[46]<<5))&(1<<6))) != 0 ||
+ not(not((W[37]^(W[41]>>25))&(1<<5))) != 0 ||
+ not((W[36]^(W[41]>>30))&(1<<0)) != 0 {
+ mask &= ^DV_II_49_2_bit
+ }
+ }
+ if (mask & DV_II_50_0_bit) != 0 {
+ if not((W[55]^W[58])&(1<<29)) != 0 ||
+ not(not((W[38]^(W[42]>>25))&(1<<3))) != 0 ||
+ not((W[37]^(W[42]<<2))&(1<<30)) != 0 {
+ mask &= ^DV_II_50_0_bit
+ }
+ }
+ if (mask & DV_II_50_2_bit) != 0 {
+ if not(not((W[54]^(W[55]<<5))&(1<<6))) != 0 ||
+ not(not((W[52]^W[54])&(1<<6))) != 0 ||
+ not((W[51]^W[55])&(1<<1)) != 0 ||
+ not((W[45]^W[47])&(1<<1)) != 0 ||
+ not(not((W[38]^(W[42]>>25))&(1<<5))) != 0 ||
+ not((W[37]^(W[42]>>30))&(1<<0)) != 0 {
+ mask &= ^DV_II_50_2_bit
+ }
+ }
+ if (mask & DV_II_51_0_bit) != 0 {
+ if not(not((W[39]^(W[43]>>25))&(1<<3))) != 0 ||
+ not((W[38]^(W[43]<<2))&(1<<30)) != 0 {
+ mask &= ^DV_II_51_0_bit
+ }
+ }
+ if (mask & DV_II_51_2_bit) != 0 {
+ if not(not((W[55]^(W[56]<<5))&(1<<6))) != 0 ||
+ not(not((W[53]^W[55])&(1<<6))) != 0 ||
+ not((W[52]^W[56])&(1<<1)) != 0 ||
+ not((W[46]^W[48])&(1<<1)) != 0 ||
+ not(not((W[39]^(W[43]>>25))&(1<<5))) != 0 ||
+ not((W[38]^(W[43]>>30))&(1<<0)) != 0 {
+ mask &= ^DV_II_51_2_bit
+ }
+ }
+ if (mask & DV_II_52_0_bit) != 0 {
+ if not(not((W[59]^W[60])&(1<<29))) != 0 ||
+ not(not((W[40]^(W[44]>>25))&(1<<3))) != 0 ||
+ not(not((W[40]^(W[44]>>25))&(1<<4))) != 0 ||
+ not((W[39]^(W[44]<<2))&(1<<30)) != 0 {
+ mask &= ^DV_II_52_0_bit
+ }
+ }
+ if (mask & DV_II_53_0_bit) != 0 {
+ if not((W[58]^W[61])&(1<<29)) != 0 ||
+ not(not((W[57]^(W[61]>>25))&(1<<4))) != 0 ||
+ not(not((W[41]^(W[45]>>25))&(1<<3))) != 0 ||
+ not(not((W[41]^(W[45]>>25))&(1<<4))) != 0 {
+ mask &= ^DV_II_53_0_bit
+ }
+ }
+ if (mask & DV_II_54_0_bit) != 0 {
+ if not(not((W[58]^(W[62]>>25))&(1<<4))) != 0 ||
+ not(not((W[42]^(W[46]>>25))&(1<<3))) != 0 ||
+ not(not((W[42]^(W[46]>>25))&(1<<4))) != 0 {
+ mask &= ^DV_II_54_0_bit
+ }
+ }
+ if (mask & DV_II_55_0_bit) != 0 {
+ if not(not((W[59]^(W[63]>>25))&(1<<4))) != 0 ||
+ not(not((W[57]^(W[59]>>25))&(1<<4))) != 0 ||
+ not(not((W[43]^(W[47]>>25))&(1<<3))) != 0 ||
+ not(not((W[43]^(W[47]>>25))&(1<<4))) != 0 {
+ mask &= ^DV_II_55_0_bit
+ }
+ }
+ if (mask & DV_II_56_0_bit) != 0 {
+ if not(not((W[60]^(W[64]>>25))&(1<<4))) != 0 ||
+ not(not((W[44]^(W[48]>>25))&(1<<3))) != 0 ||
+ not(not((W[44]^(W[48]>>25))&(1<<4))) != 0 {
+ mask &= ^DV_II_56_0_bit
+ }
+ }
+ }
+
+ return mask
+}
+
+func not(x uint32) uint32 {
+ if x == 0 {
+ return 1
+ }
+
+ return 0
+}
+
+func SHA1_dvs() []DvInfo {
+ return sha1_dvs
+}
diff --git a/vendor/github.com/pjbgf/sha1cd/ubc/const.go b/vendor/github.com/pjbgf/sha1cd/ubc/const.go
new file mode 100644
index 00000000..eac14f46
--- /dev/null
+++ b/vendor/github.com/pjbgf/sha1cd/ubc/const.go
@@ -0,0 +1,624 @@
+// Based on the C implementation from Marc Stevens and Dan Shumow.
+// https://github.com/cr-marcstevens/sha1collisiondetection
+
+package ubc
+
+const (
+ CheckSize = 80
+
+ DV_I_43_0_bit = (uint32)(1 << 0)
+ DV_I_44_0_bit = (uint32)(1 << 1)
+ DV_I_45_0_bit = (uint32)(1 << 2)
+ DV_I_46_0_bit = (uint32)(1 << 3)
+ DV_I_46_2_bit = (uint32)(1 << 4)
+ DV_I_47_0_bit = (uint32)(1 << 5)
+ DV_I_47_2_bit = (uint32)(1 << 6)
+ DV_I_48_0_bit = (uint32)(1 << 7)
+ DV_I_48_2_bit = (uint32)(1 << 8)
+ DV_I_49_0_bit = (uint32)(1 << 9)
+ DV_I_49_2_bit = (uint32)(1 << 10)
+ DV_I_50_0_bit = (uint32)(1 << 11)
+ DV_I_50_2_bit = (uint32)(1 << 12)
+ DV_I_51_0_bit = (uint32)(1 << 13)
+ DV_I_51_2_bit = (uint32)(1 << 14)
+ DV_I_52_0_bit = (uint32)(1 << 15)
+ DV_II_45_0_bit = (uint32)(1 << 16)
+ DV_II_46_0_bit = (uint32)(1 << 17)
+ DV_II_46_2_bit = (uint32)(1 << 18)
+ DV_II_47_0_bit = (uint32)(1 << 19)
+ DV_II_48_0_bit = (uint32)(1 << 20)
+ DV_II_49_0_bit = (uint32)(1 << 21)
+ DV_II_49_2_bit = (uint32)(1 << 22)
+ DV_II_50_0_bit = (uint32)(1 << 23)
+ DV_II_50_2_bit = (uint32)(1 << 24)
+ DV_II_51_0_bit = (uint32)(1 << 25)
+ DV_II_51_2_bit = (uint32)(1 << 26)
+ DV_II_52_0_bit = (uint32)(1 << 27)
+ DV_II_53_0_bit = (uint32)(1 << 28)
+ DV_II_54_0_bit = (uint32)(1 << 29)
+ DV_II_55_0_bit = (uint32)(1 << 30)
+ DV_II_56_0_bit = (uint32)(1 << 31)
+)
+
+// sha1_dvs contains a list of SHA-1 Disturbance Vectors (DV) which defines the
+// unavoidable bit conditions when a collision attack is in progress.
+var sha1_dvs = []DvInfo{
+ {
+ DvType: 1, DvK: 43, DvB: 0, TestT: 58, MaskI: 0, MaskB: 0,
+ Dm: [CheckSize]uint32{
+ 0x08000000, 0x9800000c, 0xd8000010, 0x08000010, 0xb8000010, 0x98000000, 0x60000000,
+ 0x00000008, 0xc0000000, 0x90000014, 0x10000010, 0xb8000014, 0x28000000, 0x20000010,
+ 0x48000000, 0x08000018, 0x60000000, 0x90000010, 0xf0000010, 0x90000008, 0xc0000000,
+ 0x90000010, 0xf0000010, 0xb0000008, 0x40000000, 0x90000000, 0xf0000010, 0x90000018,
+ 0x60000000, 0x90000010, 0x90000010, 0x90000000, 0x80000000, 0x00000010, 0xa0000000,
+ 0x20000000, 0xa0000000, 0x20000010, 0x00000000, 0x20000010, 0x20000000, 0x00000010,
+ 0x20000000, 0x00000010, 0xa0000000, 0x00000000, 0x20000000, 0x20000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000001, 0x00000020, 0x00000001, 0x40000002, 0x40000040,
+ 0x40000002, 0x80000004, 0x80000080, 0x80000006, 0x00000049, 0x00000103, 0x80000009,
+ 0x80000012, 0x80000202, 0x00000018, 0x00000164, 0x00000408, 0x800000e6, 0x8000004c,
+ 0x00000803, 0x80000161, 0x80000599},
+ }, {
+ DvType: 1, DvK: 44, DvB: 0, TestT: 58, MaskI: 0, MaskB: 1,
+ Dm: [CheckSize]uint32{
+ 0xb4000008, 0x08000000, 0x9800000c, 0xd8000010, 0x08000010, 0xb8000010, 0x98000000,
+ 0x60000000, 0x00000008, 0xc0000000, 0x90000014, 0x10000010, 0xb8000014, 0x28000000,
+ 0x20000010, 0x48000000, 0x08000018, 0x60000000, 0x90000010, 0xf0000010, 0x90000008,
+ 0xc0000000, 0x90000010, 0xf0000010, 0xb0000008, 0x40000000, 0x90000000, 0xf0000010,
+ 0x90000018, 0x60000000, 0x90000010, 0x90000010, 0x90000000, 0x80000000, 0x00000010,
+ 0xa0000000, 0x20000000, 0xa0000000, 0x20000010, 0x00000000, 0x20000010, 0x20000000,
+ 0x00000010, 0x20000000, 0x00000010, 0xa0000000, 0x00000000, 0x20000000, 0x20000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000020, 0x00000001, 0x40000002,
+ 0x40000040, 0x40000002, 0x80000004, 0x80000080, 0x80000006, 0x00000049, 0x00000103,
+ 0x80000009, 0x80000012, 0x80000202, 0x00000018, 0x00000164, 0x00000408, 0x800000e6,
+ 0x8000004c, 0x00000803, 0x80000161},
+ },
+ {
+ DvType: 1, DvK: 45, DvB: 0, TestT: 58, MaskI: 0, MaskB: 2,
+ Dm: [CheckSize]uint32{
+ 0xf4000014, 0xb4000008, 0x08000000, 0x9800000c, 0xd8000010, 0x08000010, 0xb8000010,
+ 0x98000000, 0x60000000, 0x00000008, 0xc0000000, 0x90000014, 0x10000010, 0xb8000014,
+ 0x28000000, 0x20000010, 0x48000000, 0x08000018, 0x60000000, 0x90000010, 0xf0000010,
+ 0x90000008, 0xc0000000, 0x90000010, 0xf0000010, 0xb0000008, 0x40000000, 0x90000000,
+ 0xf0000010, 0x90000018, 0x60000000, 0x90000010, 0x90000010, 0x90000000, 0x80000000,
+ 0x00000010, 0xa0000000, 0x20000000, 0xa0000000, 0x20000010, 0x00000000, 0x20000010,
+ 0x20000000, 0x00000010, 0x20000000, 0x00000010, 0xa0000000, 0x00000000, 0x20000000,
+ 0x20000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000020, 0x00000001,
+ 0x40000002, 0x40000040, 0x40000002, 0x80000004, 0x80000080, 0x80000006, 0x00000049,
+ 0x00000103, 0x80000009, 0x80000012, 0x80000202, 0x00000018, 0x00000164, 0x00000408,
+ 0x800000e6, 0x8000004c, 0x00000803},
+ },
+ {
+ DvType: 1, DvK: 46, DvB: 0, TestT: 58, MaskI: 0, MaskB: 3,
+ Dm: [CheckSize]uint32{
+ 0x2c000010, 0xf4000014, 0xb4000008, 0x08000000, 0x9800000c, 0xd8000010, 0x08000010,
+ 0xb8000010, 0x98000000, 0x60000000, 0x00000008, 0xc0000000, 0x90000014, 0x10000010,
+ 0xb8000014, 0x28000000, 0x20000010, 0x48000000, 0x08000018, 0x60000000, 0x90000010,
+ 0xf0000010, 0x90000008, 0xc0000000, 0x90000010, 0xf0000010, 0xb0000008, 0x40000000,
+ 0x90000000, 0xf0000010, 0x90000018, 0x60000000, 0x90000010, 0x90000010, 0x90000000,
+ 0x80000000, 0x00000010, 0xa0000000, 0x20000000, 0xa0000000, 0x20000010, 0x00000000,
+ 0x20000010, 0x20000000, 0x00000010, 0x20000000, 0x00000010, 0xa0000000, 0x00000000,
+ 0x20000000, 0x20000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000020,
+ 0x00000001, 0x40000002, 0x40000040, 0x40000002, 0x80000004, 0x80000080, 0x80000006,
+ 0x00000049, 0x00000103, 0x80000009, 0x80000012, 0x80000202, 0x00000018, 0x00000164,
+ 0x00000408, 0x800000e6, 0x8000004c},
+ },
+ {
+ DvType: 1, DvK: 46, DvB: 2, TestT: 58, MaskI: 0, MaskB: 4,
+ Dm: [CheckSize]uint32{
+ 0xb0000040, 0xd0000053, 0xd0000022, 0x20000000, 0x60000032, 0x60000043,
+ 0x20000040, 0xe0000042, 0x60000002, 0x80000001, 0x00000020, 0x00000003,
+ 0x40000052, 0x40000040, 0xe0000052, 0xa0000000, 0x80000040, 0x20000001,
+ 0x20000060, 0x80000001, 0x40000042, 0xc0000043, 0x40000022, 0x00000003,
+ 0x40000042, 0xc0000043, 0xc0000022, 0x00000001, 0x40000002, 0xc0000043,
+ 0x40000062, 0x80000001, 0x40000042, 0x40000042, 0x40000002, 0x00000002,
+ 0x00000040, 0x80000002, 0x80000000, 0x80000002, 0x80000040, 0x00000000,
+ 0x80000040, 0x80000000, 0x00000040, 0x80000000, 0x00000040, 0x80000002,
+ 0x00000000, 0x80000000, 0x80000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000004, 0x00000080, 0x00000004, 0x00000009, 0x00000101,
+ 0x00000009, 0x00000012, 0x00000202, 0x0000001a, 0x00000124, 0x0000040c,
+ 0x00000026, 0x0000004a, 0x0000080a, 0x00000060, 0x00000590, 0x00001020,
+ 0x0000039a, 0x00000132},
+ },
+ {
+ DvType: 1, DvK: 47, DvB: 0, TestT: 58, MaskI: 0, MaskB: 5,
+ Dm: [CheckSize]uint32{
+ 0xc8000010, 0x2c000010, 0xf4000014, 0xb4000008, 0x08000000, 0x9800000c,
+ 0xd8000010, 0x08000010, 0xb8000010, 0x98000000, 0x60000000, 0x00000008,
+ 0xc0000000, 0x90000014, 0x10000010, 0xb8000014, 0x28000000, 0x20000010,
+ 0x48000000, 0x08000018, 0x60000000, 0x90000010, 0xf0000010, 0x90000008,
+ 0xc0000000, 0x90000010, 0xf0000010, 0xb0000008, 0x40000000, 0x90000000,
+ 0xf0000010, 0x90000018, 0x60000000, 0x90000010, 0x90000010, 0x90000000,
+ 0x80000000, 0x00000010, 0xa0000000, 0x20000000, 0xa0000000, 0x20000010,
+ 0x00000000, 0x20000010, 0x20000000, 0x00000010, 0x20000000, 0x00000010,
+ 0xa0000000, 0x00000000, 0x20000000, 0x20000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000001, 0x00000020, 0x00000001, 0x40000002,
+ 0x40000040, 0x40000002, 0x80000004, 0x80000080, 0x80000006, 0x00000049,
+ 0x00000103, 0x80000009, 0x80000012, 0x80000202, 0x00000018, 0x00000164,
+ 0x00000408, 0x800000e6},
+ },
+ {
+ DvType: 1, DvK: 47, DvB: 2, TestT: 58, MaskI: 0, MaskB: 6,
+ Dm: [CheckSize]uint32{
+ 0x20000043, 0xb0000040, 0xd0000053, 0xd0000022, 0x20000000, 0x60000032,
+ 0x60000043, 0x20000040, 0xe0000042, 0x60000002, 0x80000001, 0x00000020,
+ 0x00000003, 0x40000052, 0x40000040, 0xe0000052, 0xa0000000, 0x80000040,
+ 0x20000001, 0x20000060, 0x80000001, 0x40000042, 0xc0000043, 0x40000022,
+ 0x00000003, 0x40000042, 0xc0000043, 0xc0000022, 0x00000001, 0x40000002,
+ 0xc0000043, 0x40000062, 0x80000001, 0x40000042, 0x40000042, 0x40000002,
+ 0x00000002, 0x00000040, 0x80000002, 0x80000000, 0x80000002, 0x80000040,
+ 0x00000000, 0x80000040, 0x80000000, 0x00000040, 0x80000000, 0x00000040,
+ 0x80000002, 0x00000000, 0x80000000, 0x80000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000004, 0x00000080, 0x00000004, 0x00000009,
+ 0x00000101, 0x00000009, 0x00000012, 0x00000202, 0x0000001a, 0x00000124,
+ 0x0000040c, 0x00000026, 0x0000004a, 0x0000080a, 0x00000060, 0x00000590,
+ 0x00001020, 0x0000039a,
+ },
+ },
+ {
+ DvType: 1, DvK: 48, DvB: 0, TestT: 58, MaskI: 0, MaskB: 7,
+ Dm: [CheckSize]uint32{
+ 0xb800000a, 0xc8000010, 0x2c000010, 0xf4000014, 0xb4000008, 0x08000000,
+ 0x9800000c, 0xd8000010, 0x08000010, 0xb8000010, 0x98000000, 0x60000000,
+ 0x00000008, 0xc0000000, 0x90000014, 0x10000010, 0xb8000014, 0x28000000,
+ 0x20000010, 0x48000000, 0x08000018, 0x60000000, 0x90000010, 0xf0000010,
+ 0x90000008, 0xc0000000, 0x90000010, 0xf0000010, 0xb0000008, 0x40000000,
+ 0x90000000, 0xf0000010, 0x90000018, 0x60000000, 0x90000010, 0x90000010,
+ 0x90000000, 0x80000000, 0x00000010, 0xa0000000, 0x20000000, 0xa0000000,
+ 0x20000010, 0x00000000, 0x20000010, 0x20000000, 0x00000010, 0x20000000,
+ 0x00000010, 0xa0000000, 0x00000000, 0x20000000, 0x20000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000020, 0x00000001,
+ 0x40000002, 0x40000040, 0x40000002, 0x80000004, 0x80000080, 0x80000006,
+ 0x00000049, 0x00000103, 0x80000009, 0x80000012, 0x80000202, 0x00000018,
+ 0x00000164, 0x00000408,
+ },
+ },
+ {
+ DvType: 1, DvK: 48, DvB: 2, TestT: 58, MaskI: 0, MaskB: 8,
+ Dm: [CheckSize]uint32{
+ 0xe000002a, 0x20000043, 0xb0000040, 0xd0000053, 0xd0000022, 0x20000000,
+ 0x60000032, 0x60000043, 0x20000040, 0xe0000042, 0x60000002, 0x80000001,
+ 0x00000020, 0x00000003, 0x40000052, 0x40000040, 0xe0000052, 0xa0000000,
+ 0x80000040, 0x20000001, 0x20000060, 0x80000001, 0x40000042, 0xc0000043,
+ 0x40000022, 0x00000003, 0x40000042, 0xc0000043, 0xc0000022, 0x00000001,
+ 0x40000002, 0xc0000043, 0x40000062, 0x80000001, 0x40000042, 0x40000042,
+ 0x40000002, 0x00000002, 0x00000040, 0x80000002, 0x80000000, 0x80000002,
+ 0x80000040, 0x00000000, 0x80000040, 0x80000000, 0x00000040, 0x80000000,
+ 0x00000040, 0x80000002, 0x00000000, 0x80000000, 0x80000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000004, 0x00000080, 0x00000004,
+ 0x00000009, 0x00000101, 0x00000009, 0x00000012, 0x00000202, 0x0000001a,
+ 0x00000124, 0x0000040c, 0x00000026, 0x0000004a, 0x0000080a, 0x00000060,
+ 0x00000590, 0x00001020},
+ },
+ {
+ DvType: 1, DvK: 49, DvB: 0, TestT: 58, MaskI: 0, MaskB: 9,
+ Dm: [CheckSize]uint32{
+ 0x18000000, 0xb800000a, 0xc8000010, 0x2c000010, 0xf4000014, 0xb4000008,
+ 0x08000000, 0x9800000c, 0xd8000010, 0x08000010, 0xb8000010, 0x98000000,
+ 0x60000000, 0x00000008, 0xc0000000, 0x90000014, 0x10000010, 0xb8000014,
+ 0x28000000, 0x20000010, 0x48000000, 0x08000018, 0x60000000, 0x90000010,
+ 0xf0000010, 0x90000008, 0xc0000000, 0x90000010, 0xf0000010, 0xb0000008,
+ 0x40000000, 0x90000000, 0xf0000010, 0x90000018, 0x60000000, 0x90000010,
+ 0x90000010, 0x90000000, 0x80000000, 0x00000010, 0xa0000000, 0x20000000,
+ 0xa0000000, 0x20000010, 0x00000000, 0x20000010, 0x20000000, 0x00000010,
+ 0x20000000, 0x00000010, 0xa0000000, 0x00000000, 0x20000000, 0x20000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000020,
+ 0x00000001, 0x40000002, 0x40000040, 0x40000002, 0x80000004, 0x80000080,
+ 0x80000006, 0x00000049, 0x00000103, 0x80000009, 0x80000012, 0x80000202,
+ 0x00000018, 0x00000164},
+ },
+ {
+ DvType: 1, DvK: 49, DvB: 2, TestT: 58, MaskI: 0, MaskB: 10,
+ Dm: [CheckSize]uint32{
+ 0x60000000, 0xe000002a, 0x20000043, 0xb0000040, 0xd0000053, 0xd0000022,
+ 0x20000000, 0x60000032, 0x60000043, 0x20000040, 0xe0000042, 0x60000002,
+ 0x80000001, 0x00000020, 0x00000003, 0x40000052, 0x40000040, 0xe0000052,
+ 0xa0000000, 0x80000040, 0x20000001, 0x20000060, 0x80000001, 0x40000042,
+ 0xc0000043, 0x40000022, 0x00000003, 0x40000042, 0xc0000043, 0xc0000022,
+ 0x00000001, 0x40000002, 0xc0000043, 0x40000062, 0x80000001, 0x40000042,
+ 0x40000042, 0x40000002, 0x00000002, 0x00000040, 0x80000002, 0x80000000,
+ 0x80000002, 0x80000040, 0x00000000, 0x80000040, 0x80000000, 0x00000040,
+ 0x80000000, 0x00000040, 0x80000002, 0x00000000, 0x80000000, 0x80000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000004, 0x00000080,
+ 0x00000004, 0x00000009, 0x00000101, 0x00000009, 0x00000012, 0x00000202,
+ 0x0000001a, 0x00000124, 0x0000040c, 0x00000026, 0x0000004a, 0x0000080a,
+ 0x00000060, 0x00000590},
+ },
+ {
+ DvType: 1, DvK: 50, DvB: 0, TestT: 65, MaskI: 0, MaskB: 11,
+ Dm: [CheckSize]uint32{
+ 0x0800000c, 0x18000000, 0xb800000a, 0xc8000010, 0x2c000010, 0xf4000014,
+ 0xb4000008, 0x08000000, 0x9800000c, 0xd8000010, 0x08000010, 0xb8000010,
+ 0x98000000, 0x60000000, 0x00000008, 0xc0000000, 0x90000014, 0x10000010,
+ 0xb8000014, 0x28000000, 0x20000010, 0x48000000, 0x08000018, 0x60000000,
+ 0x90000010, 0xf0000010, 0x90000008, 0xc0000000, 0x90000010, 0xf0000010,
+ 0xb0000008, 0x40000000, 0x90000000, 0xf0000010, 0x90000018, 0x60000000,
+ 0x90000010, 0x90000010, 0x90000000, 0x80000000, 0x00000010, 0xa0000000,
+ 0x20000000, 0xa0000000, 0x20000010, 0x00000000, 0x20000010, 0x20000000,
+ 0x00000010, 0x20000000, 0x00000010, 0xa0000000, 0x00000000, 0x20000000,
+ 0x20000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001,
+ 0x00000020, 0x00000001, 0x40000002, 0x40000040, 0x40000002, 0x80000004,
+ 0x80000080, 0x80000006, 0x00000049, 0x00000103, 0x80000009, 0x80000012,
+ 0x80000202, 0x00000018,
+ },
+ },
+ {
+ DvType: 1, DvK: 50, DvB: 2, TestT: 65, MaskI: 0, MaskB: 12,
+ Dm: [CheckSize]uint32{
+ 0x20000030, 0x60000000, 0xe000002a, 0x20000043, 0xb0000040, 0xd0000053,
+ 0xd0000022, 0x20000000, 0x60000032, 0x60000043, 0x20000040, 0xe0000042,
+ 0x60000002, 0x80000001, 0x00000020, 0x00000003, 0x40000052, 0x40000040,
+ 0xe0000052, 0xa0000000, 0x80000040, 0x20000001, 0x20000060, 0x80000001,
+ 0x40000042, 0xc0000043, 0x40000022, 0x00000003, 0x40000042, 0xc0000043,
+ 0xc0000022, 0x00000001, 0x40000002, 0xc0000043, 0x40000062, 0x80000001,
+ 0x40000042, 0x40000042, 0x40000002, 0x00000002, 0x00000040, 0x80000002,
+ 0x80000000, 0x80000002, 0x80000040, 0x00000000, 0x80000040, 0x80000000,
+ 0x00000040, 0x80000000, 0x00000040, 0x80000002, 0x00000000, 0x80000000,
+ 0x80000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000004,
+ 0x00000080, 0x00000004, 0x00000009, 0x00000101, 0x00000009, 0x00000012,
+ 0x00000202, 0x0000001a, 0x00000124, 0x0000040c, 0x00000026, 0x0000004a,
+ 0x0000080a, 0x00000060},
+ },
+ {
+ DvType: 1, DvK: 51, DvB: 0, TestT: 65, MaskI: 0, MaskB: 13,
+ Dm: [CheckSize]uint32{
+ 0xe8000000, 0x0800000c, 0x18000000, 0xb800000a, 0xc8000010, 0x2c000010,
+ 0xf4000014, 0xb4000008, 0x08000000, 0x9800000c, 0xd8000010, 0x08000010,
+ 0xb8000010, 0x98000000, 0x60000000, 0x00000008, 0xc0000000, 0x90000014,
+ 0x10000010, 0xb8000014, 0x28000000, 0x20000010, 0x48000000, 0x08000018,
+ 0x60000000, 0x90000010, 0xf0000010, 0x90000008, 0xc0000000, 0x90000010,
+ 0xf0000010, 0xb0000008, 0x40000000, 0x90000000, 0xf0000010, 0x90000018,
+ 0x60000000, 0x90000010, 0x90000010, 0x90000000, 0x80000000, 0x00000010,
+ 0xa0000000, 0x20000000, 0xa0000000, 0x20000010, 0x00000000, 0x20000010,
+ 0x20000000, 0x00000010, 0x20000000, 0x00000010, 0xa0000000, 0x00000000,
+ 0x20000000, 0x20000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000001, 0x00000020, 0x00000001, 0x40000002, 0x40000040, 0x40000002,
+ 0x80000004, 0x80000080, 0x80000006, 0x00000049, 0x00000103, 0x80000009,
+ 0x80000012, 0x80000202},
+ },
+ {
+ DvType: 1, DvK: 51, DvB: 2, TestT: 65, MaskI: 0, MaskB: 14,
+ Dm: [CheckSize]uint32{
+ 0xa0000003, 0x20000030, 0x60000000, 0xe000002a, 0x20000043, 0xb0000040,
+ 0xd0000053, 0xd0000022, 0x20000000, 0x60000032, 0x60000043, 0x20000040,
+ 0xe0000042, 0x60000002, 0x80000001, 0x00000020, 0x00000003, 0x40000052,
+ 0x40000040, 0xe0000052, 0xa0000000, 0x80000040, 0x20000001, 0x20000060,
+ 0x80000001, 0x40000042, 0xc0000043, 0x40000022, 0x00000003, 0x40000042,
+ 0xc0000043, 0xc0000022, 0x00000001, 0x40000002, 0xc0000043, 0x40000062,
+ 0x80000001, 0x40000042, 0x40000042, 0x40000002, 0x00000002, 0x00000040,
+ 0x80000002, 0x80000000, 0x80000002, 0x80000040, 0x00000000, 0x80000040,
+ 0x80000000, 0x00000040, 0x80000000, 0x00000040, 0x80000002, 0x00000000,
+ 0x80000000, 0x80000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000004, 0x00000080, 0x00000004, 0x00000009, 0x00000101, 0x00000009,
+ 0x00000012, 0x00000202, 0x0000001a, 0x00000124, 0x0000040c, 0x00000026,
+ 0x0000004a, 0x0000080a},
+ },
+ {
+ DvType: 1, DvK: 52, DvB: 0, TestT: 65, MaskI: 0, MaskB: 15,
+ Dm: [CheckSize]uint32{
+ 0x04000010, 0xe8000000, 0x0800000c, 0x18000000, 0xb800000a, 0xc8000010,
+ 0x2c000010, 0xf4000014, 0xb4000008, 0x08000000, 0x9800000c, 0xd8000010,
+ 0x08000010, 0xb8000010, 0x98000000, 0x60000000, 0x00000008, 0xc0000000,
+ 0x90000014, 0x10000010, 0xb8000014, 0x28000000, 0x20000010, 0x48000000,
+ 0x08000018, 0x60000000, 0x90000010, 0xf0000010, 0x90000008, 0xc0000000,
+ 0x90000010, 0xf0000010, 0xb0000008, 0x40000000, 0x90000000, 0xf0000010,
+ 0x90000018, 0x60000000, 0x90000010, 0x90000010, 0x90000000, 0x80000000,
+ 0x00000010, 0xa0000000, 0x20000000, 0xa0000000, 0x20000010, 0x00000000,
+ 0x20000010, 0x20000000, 0x00000010, 0x20000000, 0x00000010, 0xa0000000,
+ 0x00000000, 0x20000000, 0x20000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000001, 0x00000020, 0x00000001, 0x40000002, 0x40000040,
+ 0x40000002, 0x80000004, 0x80000080, 0x80000006, 0x00000049, 0x00000103,
+ 0x80000009, 0x80000012},
+ },
+ {
+ DvType: 2, DvK: 45, DvB: 0, TestT: 58, MaskI: 0, MaskB: 16,
+ Dm: [CheckSize]uint32{
+ 0xec000014, 0x0c000002, 0xc0000010, 0xb400001c, 0x2c000004, 0xbc000018,
+ 0xb0000010, 0x0000000c, 0xb8000010, 0x08000018, 0x78000010, 0x08000014,
+ 0x70000010, 0xb800001c, 0xe8000000, 0xb0000004, 0x58000010, 0xb000000c,
+ 0x48000000, 0xb0000000, 0xb8000010, 0x98000010, 0xa0000000, 0x00000000,
+ 0x00000000, 0x20000000, 0x80000000, 0x00000010, 0x00000000, 0x20000010,
+ 0x20000000, 0x00000010, 0x60000000, 0x00000018, 0xe0000000, 0x90000000,
+ 0x30000010, 0xb0000000, 0x20000000, 0x20000000, 0xa0000000, 0x00000010,
+ 0x80000000, 0x20000000, 0x20000000, 0x20000000, 0x80000000, 0x00000010,
+ 0x00000000, 0x20000010, 0xa0000000, 0x00000000, 0x20000000, 0x20000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000001, 0x00000020, 0x00000001, 0x40000002, 0x40000041, 0x40000022,
+ 0x80000005, 0xc0000082, 0xc0000046, 0x4000004b, 0x80000107, 0x00000089,
+ 0x00000014, 0x8000024b, 0x0000011b, 0x8000016d, 0x8000041a, 0x000002e4,
+ 0x80000054, 0x00000967},
+ },
+ {
+ DvType: 2, DvK: 46, DvB: 0, TestT: 58, MaskI: 0, MaskB: 17,
+ Dm: [CheckSize]uint32{
+ 0x2400001c, 0xec000014, 0x0c000002, 0xc0000010, 0xb400001c, 0x2c000004,
+ 0xbc000018, 0xb0000010, 0x0000000c, 0xb8000010, 0x08000018, 0x78000010,
+ 0x08000014, 0x70000010, 0xb800001c, 0xe8000000, 0xb0000004, 0x58000010,
+ 0xb000000c, 0x48000000, 0xb0000000, 0xb8000010, 0x98000010, 0xa0000000,
+ 0x00000000, 0x00000000, 0x20000000, 0x80000000, 0x00000010, 0x00000000,
+ 0x20000010, 0x20000000, 0x00000010, 0x60000000, 0x00000018, 0xe0000000,
+ 0x90000000, 0x30000010, 0xb0000000, 0x20000000, 0x20000000, 0xa0000000,
+ 0x00000010, 0x80000000, 0x20000000, 0x20000000, 0x20000000, 0x80000000,
+ 0x00000010, 0x00000000, 0x20000010, 0xa0000000, 0x00000000, 0x20000000,
+ 0x20000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000001, 0x00000020, 0x00000001, 0x40000002, 0x40000041,
+ 0x40000022, 0x80000005, 0xc0000082, 0xc0000046, 0x4000004b, 0x80000107,
+ 0x00000089, 0x00000014, 0x8000024b, 0x0000011b, 0x8000016d, 0x8000041a,
+ 0x000002e4, 0x80000054},
+ },
+ {
+ DvType: 2, DvK: 46, DvB: 2, TestT: 58, MaskI: 0, MaskB: 18,
+ Dm: [CheckSize]uint32{
+ 0x90000070, 0xb0000053, 0x30000008, 0x00000043, 0xd0000072, 0xb0000010,
+ 0xf0000062, 0xc0000042, 0x00000030, 0xe0000042, 0x20000060, 0xe0000041,
+ 0x20000050, 0xc0000041, 0xe0000072, 0xa0000003, 0xc0000012, 0x60000041,
+ 0xc0000032, 0x20000001, 0xc0000002, 0xe0000042, 0x60000042, 0x80000002,
+ 0x00000000, 0x00000000, 0x80000000, 0x00000002, 0x00000040, 0x00000000,
+ 0x80000040, 0x80000000, 0x00000040, 0x80000001, 0x00000060, 0x80000003,
+ 0x40000002, 0xc0000040, 0xc0000002, 0x80000000, 0x80000000, 0x80000002,
+ 0x00000040, 0x00000002, 0x80000000, 0x80000000, 0x80000000, 0x00000002,
+ 0x00000040, 0x00000000, 0x80000040, 0x80000002, 0x00000000, 0x80000000,
+ 0x80000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000004, 0x00000080, 0x00000004, 0x00000009, 0x00000105,
+ 0x00000089, 0x00000016, 0x0000020b, 0x0000011b, 0x0000012d, 0x0000041e,
+ 0x00000224, 0x00000050, 0x0000092e, 0x0000046c, 0x000005b6, 0x0000106a,
+ 0x00000b90, 0x00000152},
+ },
+ {
+ DvType: 2, DvK: 47, DvB: 0, TestT: 58, MaskI: 0, MaskB: 19,
+ Dm: [CheckSize]uint32{
+ 0x20000010, 0x2400001c, 0xec000014, 0x0c000002, 0xc0000010, 0xb400001c,
+ 0x2c000004, 0xbc000018, 0xb0000010, 0x0000000c, 0xb8000010, 0x08000018,
+ 0x78000010, 0x08000014, 0x70000010, 0xb800001c, 0xe8000000, 0xb0000004,
+ 0x58000010, 0xb000000c, 0x48000000, 0xb0000000, 0xb8000010, 0x98000010,
+ 0xa0000000, 0x00000000, 0x00000000, 0x20000000, 0x80000000, 0x00000010,
+ 0x00000000, 0x20000010, 0x20000000, 0x00000010, 0x60000000, 0x00000018,
+ 0xe0000000, 0x90000000, 0x30000010, 0xb0000000, 0x20000000, 0x20000000,
+ 0xa0000000, 0x00000010, 0x80000000, 0x20000000, 0x20000000, 0x20000000,
+ 0x80000000, 0x00000010, 0x00000000, 0x20000010, 0xa0000000, 0x00000000,
+ 0x20000000, 0x20000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000001, 0x00000020, 0x00000001, 0x40000002,
+ 0x40000041, 0x40000022, 0x80000005, 0xc0000082, 0xc0000046, 0x4000004b,
+ 0x80000107, 0x00000089, 0x00000014, 0x8000024b, 0x0000011b, 0x8000016d,
+ 0x8000041a, 0x000002e4},
+ },
+ {
+ DvType: 2, DvK: 48, DvB: 0, TestT: 58, MaskI: 0, MaskB: 20,
+ Dm: [CheckSize]uint32{
+ 0xbc00001a, 0x20000010, 0x2400001c, 0xec000014, 0x0c000002, 0xc0000010,
+ 0xb400001c, 0x2c000004, 0xbc000018, 0xb0000010, 0x0000000c, 0xb8000010,
+ 0x08000018, 0x78000010, 0x08000014, 0x70000010, 0xb800001c, 0xe8000000,
+ 0xb0000004, 0x58000010, 0xb000000c, 0x48000000, 0xb0000000, 0xb8000010,
+ 0x98000010, 0xa0000000, 0x00000000, 0x00000000, 0x20000000, 0x80000000,
+ 0x00000010, 0x00000000, 0x20000010, 0x20000000, 0x00000010, 0x60000000,
+ 0x00000018, 0xe0000000, 0x90000000, 0x30000010, 0xb0000000, 0x20000000,
+ 0x20000000, 0xa0000000, 0x00000010, 0x80000000, 0x20000000, 0x20000000,
+ 0x20000000, 0x80000000, 0x00000010, 0x00000000, 0x20000010, 0xa0000000,
+ 0x00000000, 0x20000000, 0x20000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000020, 0x00000001,
+ 0x40000002, 0x40000041, 0x40000022, 0x80000005, 0xc0000082, 0xc0000046,
+ 0x4000004b, 0x80000107, 0x00000089, 0x00000014, 0x8000024b, 0x0000011b,
+ 0x8000016d, 0x8000041a},
+ },
+ {
+ DvType: 2, DvK: 49, DvB: 0, TestT: 58, MaskI: 0, MaskB: 21,
+ Dm: [CheckSize]uint32{
+ 0x3c000004, 0xbc00001a, 0x20000010, 0x2400001c, 0xec000014, 0x0c000002,
+ 0xc0000010, 0xb400001c, 0x2c000004, 0xbc000018, 0xb0000010, 0x0000000c,
+ 0xb8000010, 0x08000018, 0x78000010, 0x08000014, 0x70000010, 0xb800001c,
+ 0xe8000000, 0xb0000004, 0x58000010, 0xb000000c, 0x48000000, 0xb0000000,
+ 0xb8000010, 0x98000010, 0xa0000000, 0x00000000, 0x00000000, 0x20000000,
+ 0x80000000, 0x00000010, 0x00000000, 0x20000010, 0x20000000, 0x00000010,
+ 0x60000000, 0x00000018, 0xe0000000, 0x90000000, 0x30000010, 0xb0000000,
+ 0x20000000, 0x20000000, 0xa0000000, 0x00000010, 0x80000000, 0x20000000,
+ 0x20000000, 0x20000000, 0x80000000, 0x00000010, 0x00000000, 0x20000010,
+ 0xa0000000, 0x00000000, 0x20000000, 0x20000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000020,
+ 0x00000001, 0x40000002, 0x40000041, 0x40000022, 0x80000005, 0xc0000082,
+ 0xc0000046, 0x4000004b, 0x80000107, 0x00000089, 0x00000014, 0x8000024b,
+ 0x0000011b, 0x8000016d},
+ },
+ {
+ DvType: 2, DvK: 49, DvB: 2, TestT: 58, MaskI: 0, MaskB: 22,
+ Dm: [CheckSize]uint32{
+ 0xf0000010, 0xf000006a, 0x80000040, 0x90000070, 0xb0000053, 0x30000008,
+ 0x00000043, 0xd0000072, 0xb0000010, 0xf0000062, 0xc0000042, 0x00000030,
+ 0xe0000042, 0x20000060, 0xe0000041, 0x20000050, 0xc0000041, 0xe0000072,
+ 0xa0000003, 0xc0000012, 0x60000041, 0xc0000032, 0x20000001, 0xc0000002,
+ 0xe0000042, 0x60000042, 0x80000002, 0x00000000, 0x00000000, 0x80000000,
+ 0x00000002, 0x00000040, 0x00000000, 0x80000040, 0x80000000, 0x00000040,
+ 0x80000001, 0x00000060, 0x80000003, 0x40000002, 0xc0000040, 0xc0000002,
+ 0x80000000, 0x80000000, 0x80000002, 0x00000040, 0x00000002, 0x80000000,
+ 0x80000000, 0x80000000, 0x00000002, 0x00000040, 0x00000000, 0x80000040,
+ 0x80000002, 0x00000000, 0x80000000, 0x80000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000004, 0x00000080,
+ 0x00000004, 0x00000009, 0x00000105, 0x00000089, 0x00000016, 0x0000020b,
+ 0x0000011b, 0x0000012d, 0x0000041e, 0x00000224, 0x00000050, 0x0000092e,
+ 0x0000046c, 0x000005b6},
+ },
+ {
+ DvType: 2, DvK: 50, DvB: 0, TestT: 65, MaskI: 0, MaskB: 23,
+ Dm: [CheckSize]uint32{
+ 0xb400001c, 0x3c000004, 0xbc00001a, 0x20000010, 0x2400001c, 0xec000014,
+ 0x0c000002, 0xc0000010, 0xb400001c, 0x2c000004, 0xbc000018, 0xb0000010,
+ 0x0000000c, 0xb8000010, 0x08000018, 0x78000010, 0x08000014, 0x70000010,
+ 0xb800001c, 0xe8000000, 0xb0000004, 0x58000010, 0xb000000c, 0x48000000,
+ 0xb0000000, 0xb8000010, 0x98000010, 0xa0000000, 0x00000000, 0x00000000,
+ 0x20000000, 0x80000000, 0x00000010, 0x00000000, 0x20000010, 0x20000000,
+ 0x00000010, 0x60000000, 0x00000018, 0xe0000000, 0x90000000, 0x30000010,
+ 0xb0000000, 0x20000000, 0x20000000, 0xa0000000, 0x00000010, 0x80000000,
+ 0x20000000, 0x20000000, 0x20000000, 0x80000000, 0x00000010, 0x00000000,
+ 0x20000010, 0xa0000000, 0x00000000, 0x20000000, 0x20000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001,
+ 0x00000020, 0x00000001, 0x40000002, 0x40000041, 0x40000022, 0x80000005,
+ 0xc0000082, 0xc0000046, 0x4000004b, 0x80000107, 0x00000089, 0x00000014,
+ 0x8000024b, 0x0000011b},
+ },
+ {
+ DvType: 2, DvK: 50, DvB: 2, TestT: 65, MaskI: 0, MaskB: 24,
+ Dm: [CheckSize]uint32{
+ 0xd0000072, 0xf0000010, 0xf000006a, 0x80000040, 0x90000070, 0xb0000053,
+ 0x30000008, 0x00000043, 0xd0000072, 0xb0000010, 0xf0000062, 0xc0000042,
+ 0x00000030, 0xe0000042, 0x20000060, 0xe0000041, 0x20000050, 0xc0000041,
+ 0xe0000072, 0xa0000003, 0xc0000012, 0x60000041, 0xc0000032, 0x20000001,
+ 0xc0000002, 0xe0000042, 0x60000042, 0x80000002, 0x00000000, 0x00000000,
+ 0x80000000, 0x00000002, 0x00000040, 0x00000000, 0x80000040, 0x80000000,
+ 0x00000040, 0x80000001, 0x00000060, 0x80000003, 0x40000002, 0xc0000040,
+ 0xc0000002, 0x80000000, 0x80000000, 0x80000002, 0x00000040, 0x00000002,
+ 0x80000000, 0x80000000, 0x80000000, 0x00000002, 0x00000040, 0x00000000,
+ 0x80000040, 0x80000002, 0x00000000, 0x80000000, 0x80000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000004,
+ 0x00000080, 0x00000004, 0x00000009, 0x00000105, 0x00000089, 0x00000016,
+ 0x0000020b, 0x0000011b, 0x0000012d, 0x0000041e, 0x00000224, 0x00000050,
+ 0x0000092e, 0x0000046c},
+ },
+ {
+ DvType: 2, DvK: 51, DvB: 0, TestT: 65, MaskI: 0, MaskB: 25,
+ Dm: [CheckSize]uint32{
+ 0xc0000010, 0xb400001c, 0x3c000004, 0xbc00001a, 0x20000010, 0x2400001c,
+ 0xec000014, 0x0c000002, 0xc0000010, 0xb400001c, 0x2c000004, 0xbc000018,
+ 0xb0000010, 0x0000000c, 0xb8000010, 0x08000018, 0x78000010, 0x08000014,
+ 0x70000010, 0xb800001c, 0xe8000000, 0xb0000004, 0x58000010, 0xb000000c,
+ 0x48000000, 0xb0000000, 0xb8000010, 0x98000010, 0xa0000000, 0x00000000,
+ 0x00000000, 0x20000000, 0x80000000, 0x00000010, 0x00000000, 0x20000010,
+ 0x20000000, 0x00000010, 0x60000000, 0x00000018, 0xe0000000, 0x90000000,
+ 0x30000010, 0xb0000000, 0x20000000, 0x20000000, 0xa0000000, 0x00000010,
+ 0x80000000, 0x20000000, 0x20000000, 0x20000000, 0x80000000, 0x00000010,
+ 0x00000000, 0x20000010, 0xa0000000, 0x00000000, 0x20000000, 0x20000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000001, 0x00000020, 0x00000001, 0x40000002, 0x40000041, 0x40000022,
+ 0x80000005, 0xc0000082, 0xc0000046, 0x4000004b, 0x80000107, 0x00000089,
+ 0x00000014, 0x8000024b},
+ },
+ {
+ DvType: 2, DvK: 51, DvB: 2, TestT: 65, MaskI: 0, MaskB: 26,
+ Dm: [CheckSize]uint32{
+ 0x00000043, 0xd0000072, 0xf0000010, 0xf000006a, 0x80000040, 0x90000070,
+ 0xb0000053, 0x30000008, 0x00000043, 0xd0000072, 0xb0000010, 0xf0000062,
+ 0xc0000042, 0x00000030, 0xe0000042, 0x20000060, 0xe0000041, 0x20000050,
+ 0xc0000041, 0xe0000072, 0xa0000003, 0xc0000012, 0x60000041, 0xc0000032,
+ 0x20000001, 0xc0000002, 0xe0000042, 0x60000042, 0x80000002, 0x00000000,
+ 0x00000000, 0x80000000, 0x00000002, 0x00000040, 0x00000000, 0x80000040,
+ 0x80000000, 0x00000040, 0x80000001, 0x00000060, 0x80000003, 0x40000002,
+ 0xc0000040, 0xc0000002, 0x80000000, 0x80000000, 0x80000002, 0x00000040,
+ 0x00000002, 0x80000000, 0x80000000, 0x80000000, 0x00000002, 0x00000040,
+ 0x00000000, 0x80000040, 0x80000002, 0x00000000, 0x80000000, 0x80000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000004, 0x00000080, 0x00000004, 0x00000009, 0x00000105, 0x00000089,
+ 0x00000016, 0x0000020b, 0x0000011b, 0x0000012d, 0x0000041e, 0x00000224,
+ 0x00000050, 0x0000092e},
+ },
+ {
+ DvType: 2, DvK: 52, DvB: 0, TestT: 65, MaskI: 0, MaskB: 27,
+ Dm: [CheckSize]uint32{
+ 0x0c000002, 0xc0000010, 0xb400001c, 0x3c000004, 0xbc00001a, 0x20000010,
+ 0x2400001c, 0xec000014, 0x0c000002, 0xc0000010, 0xb400001c, 0x2c000004,
+ 0xbc000018, 0xb0000010, 0x0000000c, 0xb8000010, 0x08000018, 0x78000010,
+ 0x08000014, 0x70000010, 0xb800001c, 0xe8000000, 0xb0000004, 0x58000010,
+ 0xb000000c, 0x48000000, 0xb0000000, 0xb8000010, 0x98000010, 0xa0000000,
+ 0x00000000, 0x00000000, 0x20000000, 0x80000000, 0x00000010, 0x00000000,
+ 0x20000010, 0x20000000, 0x00000010, 0x60000000, 0x00000018, 0xe0000000,
+ 0x90000000, 0x30000010, 0xb0000000, 0x20000000, 0x20000000, 0xa0000000,
+ 0x00000010, 0x80000000, 0x20000000, 0x20000000, 0x20000000, 0x80000000,
+ 0x00000010, 0x00000000, 0x20000010, 0xa0000000, 0x00000000, 0x20000000,
+ 0x20000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000001, 0x00000020, 0x00000001, 0x40000002, 0x40000041,
+ 0x40000022, 0x80000005, 0xc0000082, 0xc0000046, 0x4000004b, 0x80000107,
+ 0x00000089, 0x00000014},
+ },
+ {
+ DvType: 2, DvK: 53, DvB: 0, TestT: 65, MaskI: 0, MaskB: 28,
+ Dm: [CheckSize]uint32{
+ 0xcc000014, 0x0c000002, 0xc0000010, 0xb400001c, 0x3c000004, 0xbc00001a,
+ 0x20000010, 0x2400001c, 0xec000014, 0x0c000002, 0xc0000010, 0xb400001c,
+ 0x2c000004, 0xbc000018, 0xb0000010, 0x0000000c, 0xb8000010, 0x08000018,
+ 0x78000010, 0x08000014, 0x70000010, 0xb800001c, 0xe8000000, 0xb0000004,
+ 0x58000010, 0xb000000c, 0x48000000, 0xb0000000, 0xb8000010, 0x98000010,
+ 0xa0000000, 0x00000000, 0x00000000, 0x20000000, 0x80000000, 0x00000010,
+ 0x00000000, 0x20000010, 0x20000000, 0x00000010, 0x60000000, 0x00000018,
+ 0xe0000000, 0x90000000, 0x30000010, 0xb0000000, 0x20000000, 0x20000000,
+ 0xa0000000, 0x00000010, 0x80000000, 0x20000000, 0x20000000, 0x20000000,
+ 0x80000000, 0x00000010, 0x00000000, 0x20000010, 0xa0000000, 0x00000000,
+ 0x20000000, 0x20000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000001, 0x00000020, 0x00000001, 0x40000002,
+ 0x40000041, 0x40000022, 0x80000005, 0xc0000082, 0xc0000046, 0x4000004b,
+ 0x80000107, 0x00000089},
+ },
+ {
+ DvType: 2, DvK: 54, DvB: 0, TestT: 65, MaskI: 0, MaskB: 29,
+ Dm: [CheckSize]uint32{
+ 0x0400001c, 0xcc000014, 0x0c000002, 0xc0000010, 0xb400001c, 0x3c000004,
+ 0xbc00001a, 0x20000010, 0x2400001c, 0xec000014, 0x0c000002, 0xc0000010,
+ 0xb400001c, 0x2c000004, 0xbc000018, 0xb0000010, 0x0000000c, 0xb8000010,
+ 0x08000018, 0x78000010, 0x08000014, 0x70000010, 0xb800001c, 0xe8000000,
+ 0xb0000004, 0x58000010, 0xb000000c, 0x48000000, 0xb0000000, 0xb8000010,
+ 0x98000010, 0xa0000000, 0x00000000, 0x00000000, 0x20000000, 0x80000000,
+ 0x00000010, 0x00000000, 0x20000010, 0x20000000, 0x00000010, 0x60000000,
+ 0x00000018, 0xe0000000, 0x90000000, 0x30000010, 0xb0000000, 0x20000000,
+ 0x20000000, 0xa0000000, 0x00000010, 0x80000000, 0x20000000, 0x20000000,
+ 0x20000000, 0x80000000, 0x00000010, 0x00000000, 0x20000010, 0xa0000000,
+ 0x00000000, 0x20000000, 0x20000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000020, 0x00000001,
+ 0x40000002, 0x40000041, 0x40000022, 0x80000005, 0xc0000082, 0xc0000046,
+ 0x4000004b, 0x80000107},
+ },
+ {
+ DvType: 2, DvK: 55, DvB: 0, TestT: 65, MaskI: 0, MaskB: 30,
+ Dm: [CheckSize]uint32{
+ 0x00000010, 0x0400001c, 0xcc000014, 0x0c000002, 0xc0000010, 0xb400001c,
+ 0x3c000004, 0xbc00001a, 0x20000010, 0x2400001c, 0xec000014, 0x0c000002,
+ 0xc0000010, 0xb400001c, 0x2c000004, 0xbc000018, 0xb0000010, 0x0000000c,
+ 0xb8000010, 0x08000018, 0x78000010, 0x08000014, 0x70000010, 0xb800001c,
+ 0xe8000000, 0xb0000004, 0x58000010, 0xb000000c, 0x48000000, 0xb0000000,
+ 0xb8000010, 0x98000010, 0xa0000000, 0x00000000, 0x00000000, 0x20000000,
+ 0x80000000, 0x00000010, 0x00000000, 0x20000010, 0x20000000, 0x00000010,
+ 0x60000000, 0x00000018, 0xe0000000, 0x90000000, 0x30000010, 0xb0000000,
+ 0x20000000, 0x20000000, 0xa0000000, 0x00000010, 0x80000000, 0x20000000,
+ 0x20000000, 0x20000000, 0x80000000, 0x00000010, 0x00000000, 0x20000010,
+ 0xa0000000, 0x00000000, 0x20000000, 0x20000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000020,
+ 0x00000001, 0x40000002, 0x40000041, 0x40000022, 0x80000005, 0xc0000082,
+ 0xc0000046, 0x4000004b},
+ },
+ {
+ DvType: 2, DvK: 56, DvB: 0, TestT: 65, MaskI: 0, MaskB: 31,
+ Dm: [CheckSize]uint32{
+ 0x2600001a, 0x00000010, 0x0400001c, 0xcc000014, 0x0c000002, 0xc0000010,
+ 0xb400001c, 0x3c000004, 0xbc00001a, 0x20000010, 0x2400001c, 0xec000014,
+ 0x0c000002, 0xc0000010, 0xb400001c, 0x2c000004, 0xbc000018, 0xb0000010,
+ 0x0000000c, 0xb8000010, 0x08000018, 0x78000010, 0x08000014, 0x70000010,
+ 0xb800001c, 0xe8000000, 0xb0000004, 0x58000010, 0xb000000c, 0x48000000,
+ 0xb0000000, 0xb8000010, 0x98000010, 0xa0000000, 0x00000000, 0x00000000,
+ 0x20000000, 0x80000000, 0x00000010, 0x00000000, 0x20000010, 0x20000000,
+ 0x00000010, 0x60000000, 0x00000018, 0xe0000000, 0x90000000, 0x30000010,
+ 0xb0000000, 0x20000000, 0x20000000, 0xa0000000, 0x00000010, 0x80000000,
+ 0x20000000, 0x20000000, 0x20000000, 0x80000000, 0x00000010, 0x00000000,
+ 0x20000010, 0xa0000000, 0x00000000, 0x20000000, 0x20000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001,
+ 0x00000020, 0x00000001, 0x40000002, 0x40000041, 0x40000022, 0x80000005,
+ 0xc0000082, 0xc0000046},
+ },
+ {
+ DvType: 0, DvK: 0, DvB: 0, TestT: 0, MaskI: 0, MaskB: 0,
+ Dm: [CheckSize]uint32{
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+}
diff --git a/vendor/github.com/pjbgf/sha1cd/ubc/doc.go b/vendor/github.com/pjbgf/sha1cd/ubc/doc.go
new file mode 100644
index 00000000..0090e36b
--- /dev/null
+++ b/vendor/github.com/pjbgf/sha1cd/ubc/doc.go
@@ -0,0 +1,3 @@
+// ubc package provides ways for SHA1 blocks to be checked for
+// Unavoidable Bit Conditions that arise from crypto analysis attacks.
+package ubc
diff --git a/vendor/github.com/pmezard/go-difflib/LICENSE b/vendor/github.com/pmezard/go-difflib/LICENSE
new file mode 100644
index 00000000..c67dad61
--- /dev/null
+++ b/vendor/github.com/pmezard/go-difflib/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2013, Patrick Mezard
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+ The names of its contributors may not be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/github.com/pmezard/go-difflib/difflib/difflib.go b/vendor/github.com/pmezard/go-difflib/difflib/difflib.go
new file mode 100644
index 00000000..003e99fa
--- /dev/null
+++ b/vendor/github.com/pmezard/go-difflib/difflib/difflib.go
@@ -0,0 +1,772 @@
+// Package difflib is a partial port of Python difflib module.
+//
+// It provides tools to compare sequences of strings and generate textual diffs.
+//
+// The following class and functions have been ported:
+//
+// - SequenceMatcher
+//
+// - unified_diff
+//
+// - context_diff
+//
+// Getting unified diffs was the main goal of the port. Keep in mind this code
+// is mostly suitable to output text differences in a human friendly way, there
+// are no guarantees generated diffs are consumable by patch(1).
+package difflib
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "io"
+ "strings"
+)
+
+func min(a, b int) int {
+ if a < b {
+ return a
+ }
+ return b
+}
+
+func max(a, b int) int {
+ if a > b {
+ return a
+ }
+ return b
+}
+
+func calculateRatio(matches, length int) float64 {
+ if length > 0 {
+ return 2.0 * float64(matches) / float64(length)
+ }
+ return 1.0
+}
+
+type Match struct {
+ A int
+ B int
+ Size int
+}
+
+type OpCode struct {
+ Tag byte
+ I1 int
+ I2 int
+ J1 int
+ J2 int
+}
+
+// SequenceMatcher compares sequence of strings. The basic
+// algorithm predates, and is a little fancier than, an algorithm
+// published in the late 1980's by Ratcliff and Obershelp under the
+// hyperbolic name "gestalt pattern matching". The basic idea is to find
+// the longest contiguous matching subsequence that contains no "junk"
+// elements (R-O doesn't address junk). The same idea is then applied
+// recursively to the pieces of the sequences to the left and to the right
+// of the matching subsequence. This does not yield minimal edit
+// sequences, but does tend to yield matches that "look right" to people.
+//
+// SequenceMatcher tries to compute a "human-friendly diff" between two
+// sequences. Unlike e.g. UNIX(tm) diff, the fundamental notion is the
+// longest *contiguous* & junk-free matching subsequence. That's what
+// catches peoples' eyes. The Windows(tm) windiff has another interesting
+// notion, pairing up elements that appear uniquely in each sequence.
+// That, and the method here, appear to yield more intuitive difference
+// reports than does diff. This method appears to be the least vulnerable
+// to synching up on blocks of "junk lines", though (like blank lines in
+// ordinary text files, or maybe "" lines in HTML files). That may be
+// because this is the only method of the 3 that has a *concept* of
+// "junk" .
+//
+// Timing: Basic R-O is cubic time worst case and quadratic time expected
+// case. SequenceMatcher is quadratic time for the worst case and has
+// expected-case behavior dependent in a complicated way on how many
+// elements the sequences have in common; best case time is linear.
+type SequenceMatcher struct {
+ a []string
+ b []string
+ b2j map[string][]int
+ IsJunk func(string) bool
+ autoJunk bool
+ bJunk map[string]struct{}
+ matchingBlocks []Match
+ fullBCount map[string]int
+ bPopular map[string]struct{}
+ opCodes []OpCode
+}
+
+func NewMatcher(a, b []string) *SequenceMatcher {
+ m := SequenceMatcher{autoJunk: true}
+ m.SetSeqs(a, b)
+ return &m
+}
+
+func NewMatcherWithJunk(a, b []string, autoJunk bool,
+ isJunk func(string) bool) *SequenceMatcher {
+
+ m := SequenceMatcher{IsJunk: isJunk, autoJunk: autoJunk}
+ m.SetSeqs(a, b)
+ return &m
+}
+
+// Set two sequences to be compared.
+func (m *SequenceMatcher) SetSeqs(a, b []string) {
+ m.SetSeq1(a)
+ m.SetSeq2(b)
+}
+
+// Set the first sequence to be compared. The second sequence to be compared is
+// not changed.
+//
+// SequenceMatcher computes and caches detailed information about the second
+// sequence, so if you want to compare one sequence S against many sequences,
+// use .SetSeq2(s) once and call .SetSeq1(x) repeatedly for each of the other
+// sequences.
+//
+// See also SetSeqs() and SetSeq2().
+func (m *SequenceMatcher) SetSeq1(a []string) {
+ if &a == &m.a {
+ return
+ }
+ m.a = a
+ m.matchingBlocks = nil
+ m.opCodes = nil
+}
+
+// Set the second sequence to be compared. The first sequence to be compared is
+// not changed.
+func (m *SequenceMatcher) SetSeq2(b []string) {
+ if &b == &m.b {
+ return
+ }
+ m.b = b
+ m.matchingBlocks = nil
+ m.opCodes = nil
+ m.fullBCount = nil
+ m.chainB()
+}
+
+func (m *SequenceMatcher) chainB() {
+ // Populate line -> index mapping
+ b2j := map[string][]int{}
+ for i, s := range m.b {
+ indices := b2j[s]
+ indices = append(indices, i)
+ b2j[s] = indices
+ }
+
+ // Purge junk elements
+ m.bJunk = map[string]struct{}{}
+ if m.IsJunk != nil {
+ junk := m.bJunk
+ for s, _ := range b2j {
+ if m.IsJunk(s) {
+ junk[s] = struct{}{}
+ }
+ }
+ for s, _ := range junk {
+ delete(b2j, s)
+ }
+ }
+
+ // Purge remaining popular elements
+ popular := map[string]struct{}{}
+ n := len(m.b)
+ if m.autoJunk && n >= 200 {
+ ntest := n/100 + 1
+ for s, indices := range b2j {
+ if len(indices) > ntest {
+ popular[s] = struct{}{}
+ }
+ }
+ for s, _ := range popular {
+ delete(b2j, s)
+ }
+ }
+ m.bPopular = popular
+ m.b2j = b2j
+}
+
+func (m *SequenceMatcher) isBJunk(s string) bool {
+ _, ok := m.bJunk[s]
+ return ok
+}
+
+// Find longest matching block in a[alo:ahi] and b[blo:bhi].
+//
+// If IsJunk is not defined:
+//
+// Return (i,j,k) such that a[i:i+k] is equal to b[j:j+k], where
+// alo <= i <= i+k <= ahi
+// blo <= j <= j+k <= bhi
+// and for all (i',j',k') meeting those conditions,
+// k >= k'
+// i <= i'
+// and if i == i', j <= j'
+//
+// In other words, of all maximal matching blocks, return one that
+// starts earliest in a, and of all those maximal matching blocks that
+// start earliest in a, return the one that starts earliest in b.
+//
+// If IsJunk is defined, first the longest matching block is
+// determined as above, but with the additional restriction that no
+// junk element appears in the block. Then that block is extended as
+// far as possible by matching (only) junk elements on both sides. So
+// the resulting block never matches on junk except as identical junk
+// happens to be adjacent to an "interesting" match.
+//
+// If no blocks match, return (alo, blo, 0).
+func (m *SequenceMatcher) findLongestMatch(alo, ahi, blo, bhi int) Match {
+ // CAUTION: stripping common prefix or suffix would be incorrect.
+ // E.g.,
+ // ab
+ // acab
+ // Longest matching block is "ab", but if common prefix is
+ // stripped, it's "a" (tied with "b"). UNIX(tm) diff does so
+ // strip, so ends up claiming that ab is changed to acab by
+ // inserting "ca" in the middle. That's minimal but unintuitive:
+ // "it's obvious" that someone inserted "ac" at the front.
+ // Windiff ends up at the same place as diff, but by pairing up
+ // the unique 'b's and then matching the first two 'a's.
+ besti, bestj, bestsize := alo, blo, 0
+
+ // find longest junk-free match
+ // during an iteration of the loop, j2len[j] = length of longest
+ // junk-free match ending with a[i-1] and b[j]
+ j2len := map[int]int{}
+ for i := alo; i != ahi; i++ {
+ // look at all instances of a[i] in b; note that because
+ // b2j has no junk keys, the loop is skipped if a[i] is junk
+ newj2len := map[int]int{}
+ for _, j := range m.b2j[m.a[i]] {
+ // a[i] matches b[j]
+ if j < blo {
+ continue
+ }
+ if j >= bhi {
+ break
+ }
+ k := j2len[j-1] + 1
+ newj2len[j] = k
+ if k > bestsize {
+ besti, bestj, bestsize = i-k+1, j-k+1, k
+ }
+ }
+ j2len = newj2len
+ }
+
+ // Extend the best by non-junk elements on each end. In particular,
+ // "popular" non-junk elements aren't in b2j, which greatly speeds
+ // the inner loop above, but also means "the best" match so far
+ // doesn't contain any junk *or* popular non-junk elements.
+ for besti > alo && bestj > blo && !m.isBJunk(m.b[bestj-1]) &&
+ m.a[besti-1] == m.b[bestj-1] {
+ besti, bestj, bestsize = besti-1, bestj-1, bestsize+1
+ }
+ for besti+bestsize < ahi && bestj+bestsize < bhi &&
+ !m.isBJunk(m.b[bestj+bestsize]) &&
+ m.a[besti+bestsize] == m.b[bestj+bestsize] {
+ bestsize += 1
+ }
+
+ // Now that we have a wholly interesting match (albeit possibly
+ // empty!), we may as well suck up the matching junk on each
+ // side of it too. Can't think of a good reason not to, and it
+ // saves post-processing the (possibly considerable) expense of
+ // figuring out what to do with it. In the case of an empty
+ // interesting match, this is clearly the right thing to do,
+ // because no other kind of match is possible in the regions.
+ for besti > alo && bestj > blo && m.isBJunk(m.b[bestj-1]) &&
+ m.a[besti-1] == m.b[bestj-1] {
+ besti, bestj, bestsize = besti-1, bestj-1, bestsize+1
+ }
+ for besti+bestsize < ahi && bestj+bestsize < bhi &&
+ m.isBJunk(m.b[bestj+bestsize]) &&
+ m.a[besti+bestsize] == m.b[bestj+bestsize] {
+ bestsize += 1
+ }
+
+ return Match{A: besti, B: bestj, Size: bestsize}
+}
+
+// Return list of triples describing matching subsequences.
+//
+// Each triple is of the form (i, j, n), and means that
+// a[i:i+n] == b[j:j+n]. The triples are monotonically increasing in
+// i and in j. It's also guaranteed that if (i, j, n) and (i', j', n') are
+// adjacent triples in the list, and the second is not the last triple in the
+// list, then i+n != i' or j+n != j'. IOW, adjacent triples never describe
+// adjacent equal blocks.
+//
+// The last triple is a dummy, (len(a), len(b), 0), and is the only
+// triple with n==0.
+func (m *SequenceMatcher) GetMatchingBlocks() []Match {
+ if m.matchingBlocks != nil {
+ return m.matchingBlocks
+ }
+
+ var matchBlocks func(alo, ahi, blo, bhi int, matched []Match) []Match
+ matchBlocks = func(alo, ahi, blo, bhi int, matched []Match) []Match {
+ match := m.findLongestMatch(alo, ahi, blo, bhi)
+ i, j, k := match.A, match.B, match.Size
+ if match.Size > 0 {
+ if alo < i && blo < j {
+ matched = matchBlocks(alo, i, blo, j, matched)
+ }
+ matched = append(matched, match)
+ if i+k < ahi && j+k < bhi {
+ matched = matchBlocks(i+k, ahi, j+k, bhi, matched)
+ }
+ }
+ return matched
+ }
+ matched := matchBlocks(0, len(m.a), 0, len(m.b), nil)
+
+ // It's possible that we have adjacent equal blocks in the
+ // matching_blocks list now.
+ nonAdjacent := []Match{}
+ i1, j1, k1 := 0, 0, 0
+ for _, b := range matched {
+ // Is this block adjacent to i1, j1, k1?
+ i2, j2, k2 := b.A, b.B, b.Size
+ if i1+k1 == i2 && j1+k1 == j2 {
+ // Yes, so collapse them -- this just increases the length of
+ // the first block by the length of the second, and the first
+ // block so lengthened remains the block to compare against.
+ k1 += k2
+ } else {
+ // Not adjacent. Remember the first block (k1==0 means it's
+ // the dummy we started with), and make the second block the
+ // new block to compare against.
+ if k1 > 0 {
+ nonAdjacent = append(nonAdjacent, Match{i1, j1, k1})
+ }
+ i1, j1, k1 = i2, j2, k2
+ }
+ }
+ if k1 > 0 {
+ nonAdjacent = append(nonAdjacent, Match{i1, j1, k1})
+ }
+
+ nonAdjacent = append(nonAdjacent, Match{len(m.a), len(m.b), 0})
+ m.matchingBlocks = nonAdjacent
+ return m.matchingBlocks
+}
+
+// Return list of 5-tuples describing how to turn a into b.
+//
+// Each tuple is of the form (tag, i1, i2, j1, j2). The first tuple
+// has i1 == j1 == 0, and remaining tuples have i1 == the i2 from the
+// tuple preceding it, and likewise for j1 == the previous j2.
+//
+// The tags are characters, with these meanings:
+//
+// 'r' (replace): a[i1:i2] should be replaced by b[j1:j2]
+//
+// 'd' (delete): a[i1:i2] should be deleted, j1==j2 in this case.
+//
+// 'i' (insert): b[j1:j2] should be inserted at a[i1:i1], i1==i2 in this case.
+//
+// 'e' (equal): a[i1:i2] == b[j1:j2]
+func (m *SequenceMatcher) GetOpCodes() []OpCode {
+ if m.opCodes != nil {
+ return m.opCodes
+ }
+ i, j := 0, 0
+ matching := m.GetMatchingBlocks()
+ opCodes := make([]OpCode, 0, len(matching))
+ for _, m := range matching {
+ // invariant: we've pumped out correct diffs to change
+ // a[:i] into b[:j], and the next matching block is
+ // a[ai:ai+size] == b[bj:bj+size]. So we need to pump
+ // out a diff to change a[i:ai] into b[j:bj], pump out
+ // the matching block, and move (i,j) beyond the match
+ ai, bj, size := m.A, m.B, m.Size
+ tag := byte(0)
+ if i < ai && j < bj {
+ tag = 'r'
+ } else if i < ai {
+ tag = 'd'
+ } else if j < bj {
+ tag = 'i'
+ }
+ if tag > 0 {
+ opCodes = append(opCodes, OpCode{tag, i, ai, j, bj})
+ }
+ i, j = ai+size, bj+size
+ // the list of matching blocks is terminated by a
+ // sentinel with size 0
+ if size > 0 {
+ opCodes = append(opCodes, OpCode{'e', ai, i, bj, j})
+ }
+ }
+ m.opCodes = opCodes
+ return m.opCodes
+}
+
+// Isolate change clusters by eliminating ranges with no changes.
+//
+// Return a generator of groups with up to n lines of context.
+// Each group is in the same format as returned by GetOpCodes().
+func (m *SequenceMatcher) GetGroupedOpCodes(n int) [][]OpCode {
+ if n < 0 {
+ n = 3
+ }
+ codes := m.GetOpCodes()
+ if len(codes) == 0 {
+ codes = []OpCode{OpCode{'e', 0, 1, 0, 1}}
+ }
+ // Fixup leading and trailing groups if they show no changes.
+ if codes[0].Tag == 'e' {
+ c := codes[0]
+ i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
+ codes[0] = OpCode{c.Tag, max(i1, i2-n), i2, max(j1, j2-n), j2}
+ }
+ if codes[len(codes)-1].Tag == 'e' {
+ c := codes[len(codes)-1]
+ i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
+ codes[len(codes)-1] = OpCode{c.Tag, i1, min(i2, i1+n), j1, min(j2, j1+n)}
+ }
+ nn := n + n
+ groups := [][]OpCode{}
+ group := []OpCode{}
+ for _, c := range codes {
+ i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
+ // End the current group and start a new one whenever
+ // there is a large range with no changes.
+ if c.Tag == 'e' && i2-i1 > nn {
+ group = append(group, OpCode{c.Tag, i1, min(i2, i1+n),
+ j1, min(j2, j1+n)})
+ groups = append(groups, group)
+ group = []OpCode{}
+ i1, j1 = max(i1, i2-n), max(j1, j2-n)
+ }
+ group = append(group, OpCode{c.Tag, i1, i2, j1, j2})
+ }
+ if len(group) > 0 && !(len(group) == 1 && group[0].Tag == 'e') {
+ groups = append(groups, group)
+ }
+ return groups
+}
+
+// Return a measure of the sequences' similarity (float in [0,1]).
+//
+// Where T is the total number of elements in both sequences, and
+// M is the number of matches, this is 2.0*M / T.
+// Note that this is 1 if the sequences are identical, and 0 if
+// they have nothing in common.
+//
+// .Ratio() is expensive to compute if you haven't already computed
+// .GetMatchingBlocks() or .GetOpCodes(), in which case you may
+// want to try .QuickRatio() or .RealQuickRation() first to get an
+// upper bound.
+func (m *SequenceMatcher) Ratio() float64 {
+ matches := 0
+ for _, m := range m.GetMatchingBlocks() {
+ matches += m.Size
+ }
+ return calculateRatio(matches, len(m.a)+len(m.b))
+}
+
+// Return an upper bound on ratio() relatively quickly.
+//
+// This isn't defined beyond that it is an upper bound on .Ratio(), and
+// is faster to compute.
+func (m *SequenceMatcher) QuickRatio() float64 {
+ // viewing a and b as multisets, set matches to the cardinality
+ // of their intersection; this counts the number of matches
+ // without regard to order, so is clearly an upper bound
+ if m.fullBCount == nil {
+ m.fullBCount = map[string]int{}
+ for _, s := range m.b {
+ m.fullBCount[s] = m.fullBCount[s] + 1
+ }
+ }
+
+ // avail[x] is the number of times x appears in 'b' less the
+ // number of times we've seen it in 'a' so far ... kinda
+ avail := map[string]int{}
+ matches := 0
+ for _, s := range m.a {
+ n, ok := avail[s]
+ if !ok {
+ n = m.fullBCount[s]
+ }
+ avail[s] = n - 1
+ if n > 0 {
+ matches += 1
+ }
+ }
+ return calculateRatio(matches, len(m.a)+len(m.b))
+}
+
+// Return an upper bound on ratio() very quickly.
+//
+// This isn't defined beyond that it is an upper bound on .Ratio(), and
+// is faster to compute than either .Ratio() or .QuickRatio().
+func (m *SequenceMatcher) RealQuickRatio() float64 {
+ la, lb := len(m.a), len(m.b)
+ return calculateRatio(min(la, lb), la+lb)
+}
+
+// Convert range to the "ed" format
+func formatRangeUnified(start, stop int) string {
+ // Per the diff spec at http://www.unix.org/single_unix_specification/
+ beginning := start + 1 // lines start numbering with one
+ length := stop - start
+ if length == 1 {
+ return fmt.Sprintf("%d", beginning)
+ }
+ if length == 0 {
+ beginning -= 1 // empty ranges begin at line just before the range
+ }
+ return fmt.Sprintf("%d,%d", beginning, length)
+}
+
+// Unified diff parameters
+type UnifiedDiff struct {
+ A []string // First sequence lines
+ FromFile string // First file name
+ FromDate string // First file time
+ B []string // Second sequence lines
+ ToFile string // Second file name
+ ToDate string // Second file time
+ Eol string // Headers end of line, defaults to LF
+ Context int // Number of context lines
+}
+
+// Compare two sequences of lines; generate the delta as a unified diff.
+//
+// Unified diffs are a compact way of showing line changes and a few
+// lines of context. The number of context lines is set by 'n' which
+// defaults to three.
+//
+// By default, the diff control lines (those with ---, +++, or @@) are
+// created with a trailing newline. This is helpful so that inputs
+// created from file.readlines() result in diffs that are suitable for
+// file.writelines() since both the inputs and outputs have trailing
+// newlines.
+//
+// For inputs that do not have trailing newlines, set the lineterm
+// argument to "" so that the output will be uniformly newline free.
+//
+// The unidiff format normally has a header for filenames and modification
+// times. Any or all of these may be specified using strings for
+// 'fromfile', 'tofile', 'fromfiledate', and 'tofiledate'.
+// The modification times are normally expressed in the ISO 8601 format.
+func WriteUnifiedDiff(writer io.Writer, diff UnifiedDiff) error {
+ buf := bufio.NewWriter(writer)
+ defer buf.Flush()
+ wf := func(format string, args ...interface{}) error {
+ _, err := buf.WriteString(fmt.Sprintf(format, args...))
+ return err
+ }
+ ws := func(s string) error {
+ _, err := buf.WriteString(s)
+ return err
+ }
+
+ if len(diff.Eol) == 0 {
+ diff.Eol = "\n"
+ }
+
+ started := false
+ m := NewMatcher(diff.A, diff.B)
+ for _, g := range m.GetGroupedOpCodes(diff.Context) {
+ if !started {
+ started = true
+ fromDate := ""
+ if len(diff.FromDate) > 0 {
+ fromDate = "\t" + diff.FromDate
+ }
+ toDate := ""
+ if len(diff.ToDate) > 0 {
+ toDate = "\t" + diff.ToDate
+ }
+ if diff.FromFile != "" || diff.ToFile != "" {
+ err := wf("--- %s%s%s", diff.FromFile, fromDate, diff.Eol)
+ if err != nil {
+ return err
+ }
+ err = wf("+++ %s%s%s", diff.ToFile, toDate, diff.Eol)
+ if err != nil {
+ return err
+ }
+ }
+ }
+ first, last := g[0], g[len(g)-1]
+ range1 := formatRangeUnified(first.I1, last.I2)
+ range2 := formatRangeUnified(first.J1, last.J2)
+ if err := wf("@@ -%s +%s @@%s", range1, range2, diff.Eol); err != nil {
+ return err
+ }
+ for _, c := range g {
+ i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
+ if c.Tag == 'e' {
+ for _, line := range diff.A[i1:i2] {
+ if err := ws(" " + line); err != nil {
+ return err
+ }
+ }
+ continue
+ }
+ if c.Tag == 'r' || c.Tag == 'd' {
+ for _, line := range diff.A[i1:i2] {
+ if err := ws("-" + line); err != nil {
+ return err
+ }
+ }
+ }
+ if c.Tag == 'r' || c.Tag == 'i' {
+ for _, line := range diff.B[j1:j2] {
+ if err := ws("+" + line); err != nil {
+ return err
+ }
+ }
+ }
+ }
+ }
+ return nil
+}
+
+// Like WriteUnifiedDiff but returns the diff a string.
+func GetUnifiedDiffString(diff UnifiedDiff) (string, error) {
+ w := &bytes.Buffer{}
+ err := WriteUnifiedDiff(w, diff)
+ return string(w.Bytes()), err
+}
+
+// Convert range to the "ed" format.
+func formatRangeContext(start, stop int) string {
+ // Per the diff spec at http://www.unix.org/single_unix_specification/
+ beginning := start + 1 // lines start numbering with one
+ length := stop - start
+ if length == 0 {
+ beginning -= 1 // empty ranges begin at line just before the range
+ }
+ if length <= 1 {
+ return fmt.Sprintf("%d", beginning)
+ }
+ return fmt.Sprintf("%d,%d", beginning, beginning+length-1)
+}
+
+type ContextDiff UnifiedDiff
+
+// Compare two sequences of lines; generate the delta as a context diff.
+//
+// Context diffs are a compact way of showing line changes and a few
+// lines of context. The number of context lines is set by diff.Context
+// which defaults to three.
+//
+// By default, the diff control lines (those with *** or ---) are
+// created with a trailing newline.
+//
+// For inputs that do not have trailing newlines, set the diff.Eol
+// argument to "" so that the output will be uniformly newline free.
+//
+// The context diff format normally has a header for filenames and
+// modification times. Any or all of these may be specified using
+// strings for diff.FromFile, diff.ToFile, diff.FromDate, diff.ToDate.
+// The modification times are normally expressed in the ISO 8601 format.
+// If not specified, the strings default to blanks.
+func WriteContextDiff(writer io.Writer, diff ContextDiff) error {
+ buf := bufio.NewWriter(writer)
+ defer buf.Flush()
+ var diffErr error
+ wf := func(format string, args ...interface{}) {
+ _, err := buf.WriteString(fmt.Sprintf(format, args...))
+ if diffErr == nil && err != nil {
+ diffErr = err
+ }
+ }
+ ws := func(s string) {
+ _, err := buf.WriteString(s)
+ if diffErr == nil && err != nil {
+ diffErr = err
+ }
+ }
+
+ if len(diff.Eol) == 0 {
+ diff.Eol = "\n"
+ }
+
+ prefix := map[byte]string{
+ 'i': "+ ",
+ 'd': "- ",
+ 'r': "! ",
+ 'e': " ",
+ }
+
+ started := false
+ m := NewMatcher(diff.A, diff.B)
+ for _, g := range m.GetGroupedOpCodes(diff.Context) {
+ if !started {
+ started = true
+ fromDate := ""
+ if len(diff.FromDate) > 0 {
+ fromDate = "\t" + diff.FromDate
+ }
+ toDate := ""
+ if len(diff.ToDate) > 0 {
+ toDate = "\t" + diff.ToDate
+ }
+ if diff.FromFile != "" || diff.ToFile != "" {
+ wf("*** %s%s%s", diff.FromFile, fromDate, diff.Eol)
+ wf("--- %s%s%s", diff.ToFile, toDate, diff.Eol)
+ }
+ }
+
+ first, last := g[0], g[len(g)-1]
+ ws("***************" + diff.Eol)
+
+ range1 := formatRangeContext(first.I1, last.I2)
+ wf("*** %s ****%s", range1, diff.Eol)
+ for _, c := range g {
+ if c.Tag == 'r' || c.Tag == 'd' {
+ for _, cc := range g {
+ if cc.Tag == 'i' {
+ continue
+ }
+ for _, line := range diff.A[cc.I1:cc.I2] {
+ ws(prefix[cc.Tag] + line)
+ }
+ }
+ break
+ }
+ }
+
+ range2 := formatRangeContext(first.J1, last.J2)
+ wf("--- %s ----%s", range2, diff.Eol)
+ for _, c := range g {
+ if c.Tag == 'r' || c.Tag == 'i' {
+ for _, cc := range g {
+ if cc.Tag == 'd' {
+ continue
+ }
+ for _, line := range diff.B[cc.J1:cc.J2] {
+ ws(prefix[cc.Tag] + line)
+ }
+ }
+ break
+ }
+ }
+ }
+ return diffErr
+}
+
+// Like WriteContextDiff but returns the diff a string.
+func GetContextDiffString(diff ContextDiff) (string, error) {
+ w := &bytes.Buffer{}
+ err := WriteContextDiff(w, diff)
+ return string(w.Bytes()), err
+}
+
+// Split a string on "\n" while preserving them. The output can be used
+// as input for UnifiedDiff and ContextDiff structures.
+func SplitLines(s string) []string {
+ lines := strings.SplitAfter(s, "\n")
+ lines[len(lines)-1] += "\n"
+ return lines
+}
diff --git a/vendor/github.com/rs/zerolog/.gitignore b/vendor/github.com/rs/zerolog/.gitignore
new file mode 100644
index 00000000..8ebe58b1
--- /dev/null
+++ b/vendor/github.com/rs/zerolog/.gitignore
@@ -0,0 +1,25 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+tmp
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+*.prof
diff --git a/vendor/github.com/rs/zerolog/CNAME b/vendor/github.com/rs/zerolog/CNAME
new file mode 100644
index 00000000..9ce57a6e
--- /dev/null
+++ b/vendor/github.com/rs/zerolog/CNAME
@@ -0,0 +1 @@
+zerolog.io
\ No newline at end of file
diff --git a/vendor/github.com/rs/zerolog/LICENSE b/vendor/github.com/rs/zerolog/LICENSE
new file mode 100644
index 00000000..677e07f7
--- /dev/null
+++ b/vendor/github.com/rs/zerolog/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 Olivier Poitrey
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/github.com/rs/zerolog/README.md b/vendor/github.com/rs/zerolog/README.md
new file mode 100644
index 00000000..1306a6c1
--- /dev/null
+++ b/vendor/github.com/rs/zerolog/README.md
@@ -0,0 +1,782 @@
+# Zero Allocation JSON Logger
+
+[](https://godoc.org/github.com/rs/zerolog) [](https://raw.githubusercontent.com/rs/zerolog/master/LICENSE) [](https://github.com/rs/zerolog/actions/workflows/test.yml) [](https://raw.githack.com/wiki/rs/zerolog/coverage.html)
+
+The zerolog package provides a fast and simple logger dedicated to JSON output.
+
+Zerolog's API is designed to provide both a great developer experience and stunning [performance](#benchmarks). Its unique chaining API allows zerolog to write JSON (or CBOR) log events by avoiding allocations and reflection.
+
+Uber's [zap](https://godoc.org/go.uber.org/zap) library pioneered this approach. Zerolog is taking this concept to the next level with a simpler to use API and even better performance.
+
+To keep the code base and the API simple, zerolog focuses on efficient structured logging only. Pretty logging on the console is made possible using the provided (but inefficient) [`zerolog.ConsoleWriter`](#pretty-logging).
+
+
+
+## Who uses zerolog
+
+Find out [who uses zerolog](https://github.com/rs/zerolog/wiki/Who-uses-zerolog) and add your company / project to the list.
+
+## Features
+
+* [Blazing fast](#benchmarks)
+* [Low to zero allocation](#benchmarks)
+* [Leveled logging](#leveled-logging)
+* [Sampling](#log-sampling)
+* [Hooks](#hooks)
+* [Contextual fields](#contextual-logging)
+* [`context.Context` integration](#contextcontext-integration)
+* [Integration with `net/http`](#integration-with-nethttp)
+* [JSON and CBOR encoding formats](#binary-encoding)
+* [Pretty logging for development](#pretty-logging)
+* [Error Logging (with optional Stacktrace)](#error-logging)
+
+## Installation
+
+```bash
+go get -u github.com/rs/zerolog/log
+```
+
+## Getting Started
+
+### Simple Logging Example
+
+For simple logging, import the global logger package **github.com/rs/zerolog/log**
+
+```go
+package main
+
+import (
+ "github.com/rs/zerolog"
+ "github.com/rs/zerolog/log"
+)
+
+func main() {
+ // UNIX Time is faster and smaller than most timestamps
+ zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
+
+ log.Print("hello world")
+}
+
+// Output: {"time":1516134303,"level":"debug","message":"hello world"}
+```
+> Note: By default log writes to `os.Stderr`
+> Note: The default log level for `log.Print` is *trace*
+
+### Contextual Logging
+
+**zerolog** allows data to be added to log messages in the form of key:value pairs. The data added to the message adds "context" about the log event that can be critical for debugging as well as myriad other purposes. An example of this is below:
+
+```go
+package main
+
+import (
+ "github.com/rs/zerolog"
+ "github.com/rs/zerolog/log"
+)
+
+func main() {
+ zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
+
+ log.Debug().
+ Str("Scale", "833 cents").
+ Float64("Interval", 833.09).
+ Msg("Fibonacci is everywhere")
+
+ log.Debug().
+ Str("Name", "Tom").
+ Send()
+}
+
+// Output: {"level":"debug","Scale":"833 cents","Interval":833.09,"time":1562212768,"message":"Fibonacci is everywhere"}
+// Output: {"level":"debug","Name":"Tom","time":1562212768}
+```
+
+> You'll note in the above example that when adding contextual fields, the fields are strongly typed. You can find the full list of supported fields [here](#standard-types)
+
+### Leveled Logging
+
+#### Simple Leveled Logging Example
+
+```go
+package main
+
+import (
+ "github.com/rs/zerolog"
+ "github.com/rs/zerolog/log"
+)
+
+func main() {
+ zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
+
+ log.Info().Msg("hello world")
+}
+
+// Output: {"time":1516134303,"level":"info","message":"hello world"}
+```
+
+> It is very important to note that when using the **zerolog** chaining API, as shown above (`log.Info().Msg("hello world"`), the chain must have either the `Msg` or `Msgf` method call. If you forget to add either of these, the log will not occur and there is no compile time error to alert you of this.
+
+**zerolog** allows for logging at the following levels (from highest to lowest):
+
+* panic (`zerolog.PanicLevel`, 5)
+* fatal (`zerolog.FatalLevel`, 4)
+* error (`zerolog.ErrorLevel`, 3)
+* warn (`zerolog.WarnLevel`, 2)
+* info (`zerolog.InfoLevel`, 1)
+* debug (`zerolog.DebugLevel`, 0)
+* trace (`zerolog.TraceLevel`, -1)
+
+You can set the Global logging level to any of these options using the `SetGlobalLevel` function in the zerolog package, passing in one of the given constants above, e.g. `zerolog.InfoLevel` would be the "info" level. Whichever level is chosen, all logs with a level greater than or equal to that level will be written. To turn off logging entirely, pass the `zerolog.Disabled` constant.
+
+#### Setting Global Log Level
+
+This example uses command-line flags to demonstrate various outputs depending on the chosen log level.
+
+```go
+package main
+
+import (
+ "flag"
+
+ "github.com/rs/zerolog"
+ "github.com/rs/zerolog/log"
+)
+
+func main() {
+ zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
+ debug := flag.Bool("debug", false, "sets log level to debug")
+
+ flag.Parse()
+
+ // Default level for this example is info, unless debug flag is present
+ zerolog.SetGlobalLevel(zerolog.InfoLevel)
+ if *debug {
+ zerolog.SetGlobalLevel(zerolog.DebugLevel)
+ }
+
+ log.Debug().Msg("This message appears only when log level set to Debug")
+ log.Info().Msg("This message appears when log level set to Debug or Info")
+
+ if e := log.Debug(); e.Enabled() {
+ // Compute log output only if enabled.
+ value := "bar"
+ e.Str("foo", value).Msg("some debug message")
+ }
+}
+```
+
+Info Output (no flag)
+
+```bash
+$ ./logLevelExample
+{"time":1516387492,"level":"info","message":"This message appears when log level set to Debug or Info"}
+```
+
+Debug Output (debug flag set)
+
+```bash
+$ ./logLevelExample -debug
+{"time":1516387573,"level":"debug","message":"This message appears only when log level set to Debug"}
+{"time":1516387573,"level":"info","message":"This message appears when log level set to Debug or Info"}
+{"time":1516387573,"level":"debug","foo":"bar","message":"some debug message"}
+```
+
+#### Logging without Level or Message
+
+You may choose to log without a specific level by using the `Log` method. You may also write without a message by setting an empty string in the `msg string` parameter of the `Msg` method. Both are demonstrated in the example below.
+
+```go
+package main
+
+import (
+ "github.com/rs/zerolog"
+ "github.com/rs/zerolog/log"
+)
+
+func main() {
+ zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
+
+ log.Log().
+ Str("foo", "bar").
+ Msg("")
+}
+
+// Output: {"time":1494567715,"foo":"bar"}
+```
+
+### Error Logging
+
+You can log errors using the `Err` method
+
+```go
+package main
+
+import (
+ "errors"
+
+ "github.com/rs/zerolog"
+ "github.com/rs/zerolog/log"
+)
+
+func main() {
+ zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
+
+ err := errors.New("seems we have an error here")
+ log.Error().Err(err).Msg("")
+}
+
+// Output: {"level":"error","error":"seems we have an error here","time":1609085256}
+```
+
+> The default field name for errors is `error`, you can change this by setting `zerolog.ErrorFieldName` to meet your needs.
+
+#### Error Logging with Stacktrace
+
+Using `github.com/pkg/errors`, you can add a formatted stacktrace to your errors.
+
+```go
+package main
+
+import (
+ "github.com/pkg/errors"
+ "github.com/rs/zerolog/pkgerrors"
+
+ "github.com/rs/zerolog"
+ "github.com/rs/zerolog/log"
+)
+
+func main() {
+ zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
+ zerolog.ErrorStackMarshaler = pkgerrors.MarshalStack
+
+ err := outer()
+ log.Error().Stack().Err(err).Msg("")
+}
+
+func inner() error {
+ return errors.New("seems we have an error here")
+}
+
+func middle() error {
+ err := inner()
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func outer() error {
+ err := middle()
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// Output: {"level":"error","stack":[{"func":"inner","line":"20","source":"errors.go"},{"func":"middle","line":"24","source":"errors.go"},{"func":"outer","line":"32","source":"errors.go"},{"func":"main","line":"15","source":"errors.go"},{"func":"main","line":"204","source":"proc.go"},{"func":"goexit","line":"1374","source":"asm_amd64.s"}],"error":"seems we have an error here","time":1609086683}
+```
+
+> zerolog.ErrorStackMarshaler must be set in order for the stack to output anything.
+
+#### Logging Fatal Messages
+
+```go
+package main
+
+import (
+ "errors"
+
+ "github.com/rs/zerolog"
+ "github.com/rs/zerolog/log"
+)
+
+func main() {
+ err := errors.New("A repo man spends his life getting into tense situations")
+ service := "myservice"
+
+ zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
+
+ log.Fatal().
+ Err(err).
+ Str("service", service).
+ Msgf("Cannot start %s", service)
+}
+
+// Output: {"time":1516133263,"level":"fatal","error":"A repo man spends his life getting into tense situations","service":"myservice","message":"Cannot start myservice"}
+// exit status 1
+```
+
+> NOTE: Using `Msgf` generates one allocation even when the logger is disabled.
+
+
+### Create logger instance to manage different outputs
+
+```go
+logger := zerolog.New(os.Stderr).With().Timestamp().Logger()
+
+logger.Info().Str("foo", "bar").Msg("hello world")
+
+// Output: {"level":"info","time":1494567715,"message":"hello world","foo":"bar"}
+```
+
+### Sub-loggers let you chain loggers with additional context
+
+```go
+sublogger := log.With().
+ Str("component", "foo").
+ Logger()
+sublogger.Info().Msg("hello world")
+
+// Output: {"level":"info","time":1494567715,"message":"hello world","component":"foo"}
+```
+
+### Pretty logging
+
+To log a human-friendly, colorized output, use `zerolog.ConsoleWriter`:
+
+```go
+log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
+
+log.Info().Str("foo", "bar").Msg("Hello world")
+
+// Output: 3:04PM INF Hello World foo=bar
+```
+
+To customize the configuration and formatting:
+
+```go
+output := zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC3339}
+output.FormatLevel = func(i interface{}) string {
+ return strings.ToUpper(fmt.Sprintf("| %-6s|", i))
+}
+output.FormatMessage = func(i interface{}) string {
+ return fmt.Sprintf("***%s****", i)
+}
+output.FormatFieldName = func(i interface{}) string {
+ return fmt.Sprintf("%s:", i)
+}
+output.FormatFieldValue = func(i interface{}) string {
+ return strings.ToUpper(fmt.Sprintf("%s", i))
+}
+
+log := zerolog.New(output).With().Timestamp().Logger()
+
+log.Info().Str("foo", "bar").Msg("Hello World")
+
+// Output: 2006-01-02T15:04:05Z07:00 | INFO | ***Hello World**** foo:BAR
+```
+
+### Sub dictionary
+
+```go
+log.Info().
+ Str("foo", "bar").
+ Dict("dict", zerolog.Dict().
+ Str("bar", "baz").
+ Int("n", 1),
+ ).Msg("hello world")
+
+// Output: {"level":"info","time":1494567715,"foo":"bar","dict":{"bar":"baz","n":1},"message":"hello world"}
+```
+
+### Customize automatic field names
+
+```go
+zerolog.TimestampFieldName = "t"
+zerolog.LevelFieldName = "l"
+zerolog.MessageFieldName = "m"
+
+log.Info().Msg("hello world")
+
+// Output: {"l":"info","t":1494567715,"m":"hello world"}
+```
+
+### Add contextual fields to the global logger
+
+```go
+log.Logger = log.With().Str("foo", "bar").Logger()
+```
+
+### Add file and line number to log
+
+Equivalent of `Llongfile`:
+
+```go
+log.Logger = log.With().Caller().Logger()
+log.Info().Msg("hello world")
+
+// Output: {"level": "info", "message": "hello world", "caller": "/go/src/your_project/some_file:21"}
+```
+
+Equivalent of `Lshortfile`:
+
+```go
+zerolog.CallerMarshalFunc = func(pc uintptr, file string, line int) string {
+ return filepath.Base(file) + ":" + strconv.Itoa(line)
+}
+log.Logger = log.With().Caller().Logger()
+log.Info().Msg("hello world")
+
+// Output: {"level": "info", "message": "hello world", "caller": "some_file:21"}
+```
+
+### Thread-safe, lock-free, non-blocking writer
+
+If your writer might be slow or not thread-safe and you need your log producers to never get slowed down by a slow writer, you can use a `diode.Writer` as follows:
+
+```go
+wr := diode.NewWriter(os.Stdout, 1000, 10*time.Millisecond, func(missed int) {
+ fmt.Printf("Logger Dropped %d messages", missed)
+ })
+log := zerolog.New(wr)
+log.Print("test")
+```
+
+You will need to install `code.cloudfoundry.org/go-diodes` to use this feature.
+
+### Log Sampling
+
+```go
+sampled := log.Sample(&zerolog.BasicSampler{N: 10})
+sampled.Info().Msg("will be logged every 10 messages")
+
+// Output: {"time":1494567715,"level":"info","message":"will be logged every 10 messages"}
+```
+
+More advanced sampling:
+
+```go
+// Will let 5 debug messages per period of 1 second.
+// Over 5 debug message, 1 every 100 debug messages are logged.
+// Other levels are not sampled.
+sampled := log.Sample(zerolog.LevelSampler{
+ DebugSampler: &zerolog.BurstSampler{
+ Burst: 5,
+ Period: 1*time.Second,
+ NextSampler: &zerolog.BasicSampler{N: 100},
+ },
+})
+sampled.Debug().Msg("hello world")
+
+// Output: {"time":1494567715,"level":"debug","message":"hello world"}
+```
+
+### Hooks
+
+```go
+type SeverityHook struct{}
+
+func (h SeverityHook) Run(e *zerolog.Event, level zerolog.Level, msg string) {
+ if level != zerolog.NoLevel {
+ e.Str("severity", level.String())
+ }
+}
+
+hooked := log.Hook(SeverityHook{})
+hooked.Warn().Msg("")
+
+// Output: {"level":"warn","severity":"warn"}
+```
+
+### Pass a sub-logger by context
+
+```go
+ctx := log.With().Str("component", "module").Logger().WithContext(ctx)
+
+log.Ctx(ctx).Info().Msg("hello world")
+
+// Output: {"component":"module","level":"info","message":"hello world"}
+```
+
+### Set as standard logger output
+
+```go
+log := zerolog.New(os.Stdout).With().
+ Str("foo", "bar").
+ Logger()
+
+stdlog.SetFlags(0)
+stdlog.SetOutput(log)
+
+stdlog.Print("hello world")
+
+// Output: {"foo":"bar","message":"hello world"}
+```
+
+### context.Context integration
+
+Go contexts are commonly passed throughout Go code, and this can help you pass
+your Logger into places it might otherwise be hard to inject. The `Logger`
+instance may be attached to Go context (`context.Context`) using
+`Logger.WithContext(ctx)` and extracted from it using `zerolog.Ctx(ctx)`.
+For example:
+
+```go
+func f() {
+ logger := zerolog.New(os.Stdout)
+ ctx := context.Background()
+
+ // Attach the Logger to the context.Context
+ ctx = logger.WithContext(ctx)
+ someFunc(ctx)
+}
+
+func someFunc(ctx context.Context) {
+ // Get Logger from the go Context. if it's nil, then
+ // `zerolog.DefaultContextLogger` is returned, if
+ // `DefaultContextLogger` is nil, then a disabled logger is returned.
+ logger := zerolog.Ctx(ctx)
+ logger.Info().Msg("Hello")
+}
+```
+
+A second form of `context.Context` integration allows you to pass the current
+context.Context into the logged event, and retrieve it from hooks. This can be
+useful to log trace and span IDs or other information stored in the go context,
+and facilitates the unification of logging and tracing in some systems:
+
+```go
+type TracingHook struct{}
+
+func (h TracingHook) Run(e *zerolog.Event, level zerolog.Level, msg string) {
+ ctx := e.GetCtx()
+ spanId := getSpanIdFromContext(ctx) // as per your tracing framework
+ e.Str("span-id", spanId)
+}
+
+func f() {
+ // Setup the logger
+ logger := zerolog.New(os.Stdout)
+ logger = logger.Hook(TracingHook{})
+
+ ctx := context.Background()
+ // Use the Ctx function to make the context available to the hook
+ logger.Info().Ctx(ctx).Msg("Hello")
+}
+```
+
+### Integration with `net/http`
+
+The `github.com/rs/zerolog/hlog` package provides some helpers to integrate zerolog with `http.Handler`.
+
+In this example we use [alice](https://github.com/justinas/alice) to install logger for better readability.
+
+```go
+log := zerolog.New(os.Stdout).With().
+ Timestamp().
+ Str("role", "my-service").
+ Str("host", host).
+ Logger()
+
+c := alice.New()
+
+// Install the logger handler with default output on the console
+c = c.Append(hlog.NewHandler(log))
+
+// Install some provided extra handler to set some request's context fields.
+// Thanks to that handler, all our logs will come with some prepopulated fields.
+c = c.Append(hlog.AccessHandler(func(r *http.Request, status, size int, duration time.Duration) {
+ hlog.FromRequest(r).Info().
+ Str("method", r.Method).
+ Stringer("url", r.URL).
+ Int("status", status).
+ Int("size", size).
+ Dur("duration", duration).
+ Msg("")
+}))
+c = c.Append(hlog.RemoteAddrHandler("ip"))
+c = c.Append(hlog.UserAgentHandler("user_agent"))
+c = c.Append(hlog.RefererHandler("referer"))
+c = c.Append(hlog.RequestIDHandler("req_id", "Request-Id"))
+
+// Here is your final handler
+h := c.Then(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ // Get the logger from the request's context. You can safely assume it
+ // will be always there: if the handler is removed, hlog.FromRequest
+ // will return a no-op logger.
+ hlog.FromRequest(r).Info().
+ Str("user", "current user").
+ Str("status", "ok").
+ Msg("Something happened")
+
+ // Output: {"level":"info","time":"2001-02-03T04:05:06Z","role":"my-service","host":"local-hostname","req_id":"b4g0l5t6tfid6dtrapu0","user":"current user","status":"ok","message":"Something happened"}
+}))
+http.Handle("/", h)
+
+if err := http.ListenAndServe(":8080", nil); err != nil {
+ log.Fatal().Err(err).Msg("Startup failed")
+}
+```
+
+## Multiple Log Output
+`zerolog.MultiLevelWriter` may be used to send the log message to multiple outputs.
+In this example, we send the log message to both `os.Stdout` and the in-built ConsoleWriter.
+```go
+func main() {
+ consoleWriter := zerolog.ConsoleWriter{Out: os.Stdout}
+
+ multi := zerolog.MultiLevelWriter(consoleWriter, os.Stdout)
+
+ logger := zerolog.New(multi).With().Timestamp().Logger()
+
+ logger.Info().Msg("Hello World!")
+}
+
+// Output (Line 1: Console; Line 2: Stdout)
+// 12:36PM INF Hello World!
+// {"level":"info","time":"2019-11-07T12:36:38+03:00","message":"Hello World!"}
+```
+
+## Global Settings
+
+Some settings can be changed and will be applied to all loggers:
+
+* `log.Logger`: You can set this value to customize the global logger (the one used by package level methods).
+* `zerolog.SetGlobalLevel`: Can raise the minimum level of all loggers. Call this with `zerolog.Disabled` to disable logging altogether (quiet mode).
+* `zerolog.DisableSampling`: If argument is `true`, all sampled loggers will stop sampling and issue 100% of their log events.
+* `zerolog.TimestampFieldName`: Can be set to customize `Timestamp` field name.
+* `zerolog.LevelFieldName`: Can be set to customize level field name.
+* `zerolog.MessageFieldName`: Can be set to customize message field name.
+* `zerolog.ErrorFieldName`: Can be set to customize `Err` field name.
+* `zerolog.TimeFieldFormat`: Can be set to customize `Time` field value formatting. If set with `zerolog.TimeFormatUnix`, `zerolog.TimeFormatUnixMs` or `zerolog.TimeFormatUnixMicro`, times are formatted as UNIX timestamp.
+* `zerolog.DurationFieldUnit`: Can be set to customize the unit for time.Duration type fields added by `Dur` (default: `time.Millisecond`).
+* `zerolog.DurationFieldInteger`: If set to `true`, `Dur` fields are formatted as integers instead of floats (default: `false`).
+* `zerolog.ErrorHandler`: Called whenever zerolog fails to write an event on its output. If not set, an error is printed on the stderr. This handler must be thread safe and non-blocking.
+* `zerolog.FloatingPointPrecision`: If set to a value other than -1, controls the number
+of digits when formatting float numbers in JSON. See
+[strconv.FormatFloat](https://pkg.go.dev/strconv#FormatFloat)
+for more details.
+
+## Field Types
+
+### Standard Types
+
+* `Str`
+* `Bool`
+* `Int`, `Int8`, `Int16`, `Int32`, `Int64`
+* `Uint`, `Uint8`, `Uint16`, `Uint32`, `Uint64`
+* `Float32`, `Float64`
+
+### Advanced Fields
+
+* `Err`: Takes an `error` and renders it as a string using the `zerolog.ErrorFieldName` field name.
+* `Func`: Run a `func` only if the level is enabled.
+* `Timestamp`: Inserts a timestamp field with `zerolog.TimestampFieldName` field name, formatted using `zerolog.TimeFieldFormat`.
+* `Time`: Adds a field with time formatted with `zerolog.TimeFieldFormat`.
+* `Dur`: Adds a field with `time.Duration`.
+* `Dict`: Adds a sub-key/value as a field of the event.
+* `RawJSON`: Adds a field with an already encoded JSON (`[]byte`)
+* `Hex`: Adds a field with value formatted as a hexadecimal string (`[]byte`)
+* `Interface`: Uses reflection to marshal the type.
+
+Most fields are also available in the slice format (`Strs` for `[]string`, `Errs` for `[]error` etc.)
+
+## Binary Encoding
+
+In addition to the default JSON encoding, `zerolog` can produce binary logs using [CBOR](https://cbor.io) encoding. The choice of encoding can be decided at compile time using the build tag `binary_log` as follows:
+
+```bash
+go build -tags binary_log .
+```
+
+To Decode binary encoded log files you can use any CBOR decoder. One has been tested to work
+with zerolog library is [CSD](https://github.com/toravir/csd/).
+
+## Related Projects
+
+* [grpc-zerolog](https://github.com/cheapRoc/grpc-zerolog): Implementation of `grpclog.LoggerV2` interface using `zerolog`
+* [overlog](https://github.com/Trendyol/overlog): Implementation of `Mapped Diagnostic Context` interface using `zerolog`
+* [zerologr](https://github.com/go-logr/zerologr): Implementation of `logr.LogSink` interface using `zerolog`
+
+## Benchmarks
+
+See [logbench](http://bench.zerolog.io/) for more comprehensive and up-to-date benchmarks.
+
+All operations are allocation free (those numbers *include* JSON encoding):
+
+```text
+BenchmarkLogEmpty-8 100000000 19.1 ns/op 0 B/op 0 allocs/op
+BenchmarkDisabled-8 500000000 4.07 ns/op 0 B/op 0 allocs/op
+BenchmarkInfo-8 30000000 42.5 ns/op 0 B/op 0 allocs/op
+BenchmarkContextFields-8 30000000 44.9 ns/op 0 B/op 0 allocs/op
+BenchmarkLogFields-8 10000000 184 ns/op 0 B/op 0 allocs/op
+```
+
+There are a few Go logging benchmarks and comparisons that include zerolog.
+
+* [imkira/go-loggers-bench](https://github.com/imkira/go-loggers-bench)
+* [uber-common/zap](https://github.com/uber-go/zap#performance)
+
+Using Uber's zap comparison benchmark:
+
+Log a message and 10 fields:
+
+| Library | Time | Bytes Allocated | Objects Allocated |
+| :--- | :---: | :---: | :---: |
+| zerolog | 767 ns/op | 552 B/op | 6 allocs/op |
+| :zap: zap | 848 ns/op | 704 B/op | 2 allocs/op |
+| :zap: zap (sugared) | 1363 ns/op | 1610 B/op | 20 allocs/op |
+| go-kit | 3614 ns/op | 2895 B/op | 66 allocs/op |
+| lion | 5392 ns/op | 5807 B/op | 63 allocs/op |
+| logrus | 5661 ns/op | 6092 B/op | 78 allocs/op |
+| apex/log | 15332 ns/op | 3832 B/op | 65 allocs/op |
+| log15 | 20657 ns/op | 5632 B/op | 93 allocs/op |
+
+Log a message with a logger that already has 10 fields of context:
+
+| Library | Time | Bytes Allocated | Objects Allocated |
+| :--- | :---: | :---: | :---: |
+| zerolog | 52 ns/op | 0 B/op | 0 allocs/op |
+| :zap: zap | 283 ns/op | 0 B/op | 0 allocs/op |
+| :zap: zap (sugared) | 337 ns/op | 80 B/op | 2 allocs/op |
+| lion | 2702 ns/op | 4074 B/op | 38 allocs/op |
+| go-kit | 3378 ns/op | 3046 B/op | 52 allocs/op |
+| logrus | 4309 ns/op | 4564 B/op | 63 allocs/op |
+| apex/log | 13456 ns/op | 2898 B/op | 51 allocs/op |
+| log15 | 14179 ns/op | 2642 B/op | 44 allocs/op |
+
+Log a static string, without any context or `printf`-style templating:
+
+| Library | Time | Bytes Allocated | Objects Allocated |
+| :--- | :---: | :---: | :---: |
+| zerolog | 50 ns/op | 0 B/op | 0 allocs/op |
+| :zap: zap | 236 ns/op | 0 B/op | 0 allocs/op |
+| standard library | 453 ns/op | 80 B/op | 2 allocs/op |
+| :zap: zap (sugared) | 337 ns/op | 80 B/op | 2 allocs/op |
+| go-kit | 508 ns/op | 656 B/op | 13 allocs/op |
+| lion | 771 ns/op | 1224 B/op | 10 allocs/op |
+| logrus | 1244 ns/op | 1505 B/op | 27 allocs/op |
+| apex/log | 2751 ns/op | 584 B/op | 11 allocs/op |
+| log15 | 5181 ns/op | 1592 B/op | 26 allocs/op |
+
+## Caveats
+
+### Field duplication
+
+Note that zerolog does no de-duplication of fields. Using the same key multiple times creates multiple keys in final JSON:
+
+```go
+logger := zerolog.New(os.Stderr).With().Timestamp().Logger()
+logger.Info().
+ Timestamp().
+ Msg("dup")
+// Output: {"level":"info","time":1494567715,"time":1494567715,"message":"dup"}
+```
+
+In this case, many consumers will take the last value, but this is not guaranteed; check yours if in doubt.
+
+### Concurrency safety
+
+Be careful when calling UpdateContext. It is not concurrency safe. Use the With method to create a child logger:
+
+```go
+func handler(w http.ResponseWriter, r *http.Request) {
+ // Create a child logger for concurrency safety
+ logger := log.Logger.With().Logger()
+
+ // Add context fields, for example User-Agent from HTTP headers
+ logger.UpdateContext(func(c zerolog.Context) zerolog.Context {
+ ...
+ })
+}
+```
diff --git a/vendor/github.com/rs/zerolog/_config.yml b/vendor/github.com/rs/zerolog/_config.yml
new file mode 100644
index 00000000..a1e896d7
--- /dev/null
+++ b/vendor/github.com/rs/zerolog/_config.yml
@@ -0,0 +1 @@
+remote_theme: rs/gh-readme
diff --git a/vendor/github.com/rs/zerolog/array.go b/vendor/github.com/rs/zerolog/array.go
new file mode 100644
index 00000000..ba35b283
--- /dev/null
+++ b/vendor/github.com/rs/zerolog/array.go
@@ -0,0 +1,240 @@
+package zerolog
+
+import (
+ "net"
+ "sync"
+ "time"
+)
+
+var arrayPool = &sync.Pool{
+ New: func() interface{} {
+ return &Array{
+ buf: make([]byte, 0, 500),
+ }
+ },
+}
+
+// Array is used to prepopulate an array of items
+// which can be re-used to add to log messages.
+type Array struct {
+ buf []byte
+}
+
+func putArray(a *Array) {
+ // Proper usage of a sync.Pool requires each entry to have approximately
+ // the same memory cost. To obtain this property when the stored type
+ // contains a variably-sized buffer, we add a hard limit on the maximum buffer
+ // to place back in the pool.
+ //
+ // See https://golang.org/issue/23199
+ const maxSize = 1 << 16 // 64KiB
+ if cap(a.buf) > maxSize {
+ return
+ }
+ arrayPool.Put(a)
+}
+
+// Arr creates an array to be added to an Event or Context.
+func Arr() *Array {
+ a := arrayPool.Get().(*Array)
+ a.buf = a.buf[:0]
+ return a
+}
+
+// MarshalZerologArray method here is no-op - since data is
+// already in the needed format.
+func (*Array) MarshalZerologArray(*Array) {
+}
+
+func (a *Array) write(dst []byte) []byte {
+ dst = enc.AppendArrayStart(dst)
+ if len(a.buf) > 0 {
+ dst = append(dst, a.buf...)
+ }
+ dst = enc.AppendArrayEnd(dst)
+ putArray(a)
+ return dst
+}
+
+// Object marshals an object that implement the LogObjectMarshaler
+// interface and appends it to the array.
+func (a *Array) Object(obj LogObjectMarshaler) *Array {
+ e := Dict()
+ obj.MarshalZerologObject(e)
+ e.buf = enc.AppendEndMarker(e.buf)
+ a.buf = append(enc.AppendArrayDelim(a.buf), e.buf...)
+ putEvent(e)
+ return a
+}
+
+// Str appends the val as a string to the array.
+func (a *Array) Str(val string) *Array {
+ a.buf = enc.AppendString(enc.AppendArrayDelim(a.buf), val)
+ return a
+}
+
+// Bytes appends the val as a string to the array.
+func (a *Array) Bytes(val []byte) *Array {
+ a.buf = enc.AppendBytes(enc.AppendArrayDelim(a.buf), val)
+ return a
+}
+
+// Hex appends the val as a hex string to the array.
+func (a *Array) Hex(val []byte) *Array {
+ a.buf = enc.AppendHex(enc.AppendArrayDelim(a.buf), val)
+ return a
+}
+
+// RawJSON adds already encoded JSON to the array.
+func (a *Array) RawJSON(val []byte) *Array {
+ a.buf = appendJSON(enc.AppendArrayDelim(a.buf), val)
+ return a
+}
+
+// Err serializes and appends the err to the array.
+func (a *Array) Err(err error) *Array {
+ switch m := ErrorMarshalFunc(err).(type) {
+ case LogObjectMarshaler:
+ e := newEvent(nil, 0)
+ e.buf = e.buf[:0]
+ e.appendObject(m)
+ a.buf = append(enc.AppendArrayDelim(a.buf), e.buf...)
+ putEvent(e)
+ case error:
+ if m == nil || isNilValue(m) {
+ a.buf = enc.AppendNil(enc.AppendArrayDelim(a.buf))
+ } else {
+ a.buf = enc.AppendString(enc.AppendArrayDelim(a.buf), m.Error())
+ }
+ case string:
+ a.buf = enc.AppendString(enc.AppendArrayDelim(a.buf), m)
+ default:
+ a.buf = enc.AppendInterface(enc.AppendArrayDelim(a.buf), m)
+ }
+
+ return a
+}
+
+// Bool appends the val as a bool to the array.
+func (a *Array) Bool(b bool) *Array {
+ a.buf = enc.AppendBool(enc.AppendArrayDelim(a.buf), b)
+ return a
+}
+
+// Int appends i as a int to the array.
+func (a *Array) Int(i int) *Array {
+ a.buf = enc.AppendInt(enc.AppendArrayDelim(a.buf), i)
+ return a
+}
+
+// Int8 appends i as a int8 to the array.
+func (a *Array) Int8(i int8) *Array {
+ a.buf = enc.AppendInt8(enc.AppendArrayDelim(a.buf), i)
+ return a
+}
+
+// Int16 appends i as a int16 to the array.
+func (a *Array) Int16(i int16) *Array {
+ a.buf = enc.AppendInt16(enc.AppendArrayDelim(a.buf), i)
+ return a
+}
+
+// Int32 appends i as a int32 to the array.
+func (a *Array) Int32(i int32) *Array {
+ a.buf = enc.AppendInt32(enc.AppendArrayDelim(a.buf), i)
+ return a
+}
+
+// Int64 appends i as a int64 to the array.
+func (a *Array) Int64(i int64) *Array {
+ a.buf = enc.AppendInt64(enc.AppendArrayDelim(a.buf), i)
+ return a
+}
+
+// Uint appends i as a uint to the array.
+func (a *Array) Uint(i uint) *Array {
+ a.buf = enc.AppendUint(enc.AppendArrayDelim(a.buf), i)
+ return a
+}
+
+// Uint8 appends i as a uint8 to the array.
+func (a *Array) Uint8(i uint8) *Array {
+ a.buf = enc.AppendUint8(enc.AppendArrayDelim(a.buf), i)
+ return a
+}
+
+// Uint16 appends i as a uint16 to the array.
+func (a *Array) Uint16(i uint16) *Array {
+ a.buf = enc.AppendUint16(enc.AppendArrayDelim(a.buf), i)
+ return a
+}
+
+// Uint32 appends i as a uint32 to the array.
+func (a *Array) Uint32(i uint32) *Array {
+ a.buf = enc.AppendUint32(enc.AppendArrayDelim(a.buf), i)
+ return a
+}
+
+// Uint64 appends i as a uint64 to the array.
+func (a *Array) Uint64(i uint64) *Array {
+ a.buf = enc.AppendUint64(enc.AppendArrayDelim(a.buf), i)
+ return a
+}
+
+// Float32 appends f as a float32 to the array.
+func (a *Array) Float32(f float32) *Array {
+ a.buf = enc.AppendFloat32(enc.AppendArrayDelim(a.buf), f, FloatingPointPrecision)
+ return a
+}
+
+// Float64 appends f as a float64 to the array.
+func (a *Array) Float64(f float64) *Array {
+ a.buf = enc.AppendFloat64(enc.AppendArrayDelim(a.buf), f, FloatingPointPrecision)
+ return a
+}
+
+// Time appends t formatted as string using zerolog.TimeFieldFormat.
+func (a *Array) Time(t time.Time) *Array {
+ a.buf = enc.AppendTime(enc.AppendArrayDelim(a.buf), t, TimeFieldFormat)
+ return a
+}
+
+// Dur appends d to the array.
+func (a *Array) Dur(d time.Duration) *Array {
+ a.buf = enc.AppendDuration(enc.AppendArrayDelim(a.buf), d, DurationFieldUnit, DurationFieldInteger, FloatingPointPrecision)
+ return a
+}
+
+// Interface appends i marshaled using reflection.
+func (a *Array) Interface(i interface{}) *Array {
+ if obj, ok := i.(LogObjectMarshaler); ok {
+ return a.Object(obj)
+ }
+ a.buf = enc.AppendInterface(enc.AppendArrayDelim(a.buf), i)
+ return a
+}
+
+// IPAddr adds IPv4 or IPv6 address to the array
+func (a *Array) IPAddr(ip net.IP) *Array {
+ a.buf = enc.AppendIPAddr(enc.AppendArrayDelim(a.buf), ip)
+ return a
+}
+
+// IPPrefix adds IPv4 or IPv6 Prefix (IP + mask) to the array
+func (a *Array) IPPrefix(pfx net.IPNet) *Array {
+ a.buf = enc.AppendIPPrefix(enc.AppendArrayDelim(a.buf), pfx)
+ return a
+}
+
+// MACAddr adds a MAC (Ethernet) address to the array
+func (a *Array) MACAddr(ha net.HardwareAddr) *Array {
+ a.buf = enc.AppendMACAddr(enc.AppendArrayDelim(a.buf), ha)
+ return a
+}
+
+// Dict adds the dict Event to the array
+func (a *Array) Dict(dict *Event) *Array {
+ dict.buf = enc.AppendEndMarker(dict.buf)
+ a.buf = append(enc.AppendArrayDelim(a.buf), dict.buf...)
+ return a
+}
diff --git a/vendor/github.com/rs/zerolog/console.go b/vendor/github.com/rs/zerolog/console.go
new file mode 100644
index 00000000..7e65e86f
--- /dev/null
+++ b/vendor/github.com/rs/zerolog/console.go
@@ -0,0 +1,520 @@
+package zerolog
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "io"
+ "os"
+ "path/filepath"
+ "sort"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/mattn/go-colorable"
+)
+
+const (
+ colorBlack = iota + 30
+ colorRed
+ colorGreen
+ colorYellow
+ colorBlue
+ colorMagenta
+ colorCyan
+ colorWhite
+
+ colorBold = 1
+ colorDarkGray = 90
+
+ unknownLevel = "???"
+)
+
+var (
+ consoleBufPool = sync.Pool{
+ New: func() interface{} {
+ return bytes.NewBuffer(make([]byte, 0, 100))
+ },
+ }
+)
+
+const (
+ consoleDefaultTimeFormat = time.Kitchen
+)
+
+// Formatter transforms the input into a formatted string.
+type Formatter func(interface{}) string
+
+// ConsoleWriter parses the JSON input and writes it in an
+// (optionally) colorized, human-friendly format to Out.
+type ConsoleWriter struct {
+ // Out is the output destination.
+ Out io.Writer
+
+ // NoColor disables the colorized output.
+ NoColor bool
+
+ // TimeFormat specifies the format for timestamp in output.
+ TimeFormat string
+
+ // TimeLocation tells ConsoleWriter’s default FormatTimestamp
+ // how to localize the time.
+ TimeLocation *time.Location
+
+ // PartsOrder defines the order of parts in output.
+ PartsOrder []string
+
+ // PartsExclude defines parts to not display in output.
+ PartsExclude []string
+
+ // FieldsOrder defines the order of contextual fields in output.
+ FieldsOrder []string
+
+ fieldIsOrdered map[string]int
+
+ // FieldsExclude defines contextual fields to not display in output.
+ FieldsExclude []string
+
+ FormatTimestamp Formatter
+ FormatLevel Formatter
+ FormatCaller Formatter
+ FormatMessage Formatter
+ FormatFieldName Formatter
+ FormatFieldValue Formatter
+ FormatErrFieldName Formatter
+ FormatErrFieldValue Formatter
+
+ FormatExtra func(map[string]interface{}, *bytes.Buffer) error
+
+ FormatPrepare func(map[string]interface{}) error
+}
+
+// NewConsoleWriter creates and initializes a new ConsoleWriter.
+func NewConsoleWriter(options ...func(w *ConsoleWriter)) ConsoleWriter {
+ w := ConsoleWriter{
+ Out: os.Stdout,
+ TimeFormat: consoleDefaultTimeFormat,
+ PartsOrder: consoleDefaultPartsOrder(),
+ }
+
+ for _, opt := range options {
+ opt(&w)
+ }
+
+ // Fix color on Windows
+ if w.Out == os.Stdout || w.Out == os.Stderr {
+ w.Out = colorable.NewColorable(w.Out.(*os.File))
+ }
+
+ return w
+}
+
+// Write transforms the JSON input with formatters and appends to w.Out.
+func (w ConsoleWriter) Write(p []byte) (n int, err error) {
+ // Fix color on Windows
+ if w.Out == os.Stdout || w.Out == os.Stderr {
+ w.Out = colorable.NewColorable(w.Out.(*os.File))
+ }
+
+ if w.PartsOrder == nil {
+ w.PartsOrder = consoleDefaultPartsOrder()
+ }
+
+ var buf = consoleBufPool.Get().(*bytes.Buffer)
+ defer func() {
+ buf.Reset()
+ consoleBufPool.Put(buf)
+ }()
+
+ var evt map[string]interface{}
+ p = decodeIfBinaryToBytes(p)
+ d := json.NewDecoder(bytes.NewReader(p))
+ d.UseNumber()
+ err = d.Decode(&evt)
+ if err != nil {
+ return n, fmt.Errorf("cannot decode event: %s", err)
+ }
+
+ if w.FormatPrepare != nil {
+ err = w.FormatPrepare(evt)
+ if err != nil {
+ return n, err
+ }
+ }
+
+ for _, p := range w.PartsOrder {
+ w.writePart(buf, evt, p)
+ }
+
+ w.writeFields(evt, buf)
+
+ if w.FormatExtra != nil {
+ err = w.FormatExtra(evt, buf)
+ if err != nil {
+ return n, err
+ }
+ }
+
+ err = buf.WriteByte('\n')
+ if err != nil {
+ return n, err
+ }
+
+ _, err = buf.WriteTo(w.Out)
+ return len(p), err
+}
+
+// Call the underlying writer's Close method if it is an io.Closer. Otherwise
+// does nothing.
+func (w ConsoleWriter) Close() error {
+ if closer, ok := w.Out.(io.Closer); ok {
+ return closer.Close()
+ }
+ return nil
+}
+
+// writeFields appends formatted key-value pairs to buf.
+func (w ConsoleWriter) writeFields(evt map[string]interface{}, buf *bytes.Buffer) {
+ var fields = make([]string, 0, len(evt))
+ for field := range evt {
+ var isExcluded bool
+ for _, excluded := range w.FieldsExclude {
+ if field == excluded {
+ isExcluded = true
+ break
+ }
+ }
+ if isExcluded {
+ continue
+ }
+
+ switch field {
+ case LevelFieldName, TimestampFieldName, MessageFieldName, CallerFieldName:
+ continue
+ }
+ fields = append(fields, field)
+ }
+
+ if len(w.FieldsOrder) > 0 {
+ w.orderFields(fields)
+ } else {
+ sort.Strings(fields)
+ }
+
+ // Write space only if something has already been written to the buffer, and if there are fields.
+ if buf.Len() > 0 && len(fields) > 0 {
+ buf.WriteByte(' ')
+ }
+
+ // Move the "error" field to the front
+ ei := sort.Search(len(fields), func(i int) bool { return fields[i] >= ErrorFieldName })
+ if ei < len(fields) && fields[ei] == ErrorFieldName {
+ fields[ei] = ""
+ fields = append([]string{ErrorFieldName}, fields...)
+ var xfields = make([]string, 0, len(fields))
+ for _, field := range fields {
+ if field == "" { // Skip empty fields
+ continue
+ }
+ xfields = append(xfields, field)
+ }
+ fields = xfields
+ }
+
+ for i, field := range fields {
+ var fn Formatter
+ var fv Formatter
+
+ if field == ErrorFieldName {
+ if w.FormatErrFieldName == nil {
+ fn = consoleDefaultFormatErrFieldName(w.NoColor)
+ } else {
+ fn = w.FormatErrFieldName
+ }
+
+ if w.FormatErrFieldValue == nil {
+ fv = consoleDefaultFormatErrFieldValue(w.NoColor)
+ } else {
+ fv = w.FormatErrFieldValue
+ }
+ } else {
+ if w.FormatFieldName == nil {
+ fn = consoleDefaultFormatFieldName(w.NoColor)
+ } else {
+ fn = w.FormatFieldName
+ }
+
+ if w.FormatFieldValue == nil {
+ fv = consoleDefaultFormatFieldValue
+ } else {
+ fv = w.FormatFieldValue
+ }
+ }
+
+ buf.WriteString(fn(field))
+
+ switch fValue := evt[field].(type) {
+ case string:
+ if needsQuote(fValue) {
+ buf.WriteString(fv(strconv.Quote(fValue)))
+ } else {
+ buf.WriteString(fv(fValue))
+ }
+ case json.Number:
+ buf.WriteString(fv(fValue))
+ default:
+ b, err := InterfaceMarshalFunc(fValue)
+ if err != nil {
+ fmt.Fprintf(buf, colorize("[error: %v]", colorRed, w.NoColor), err)
+ } else {
+ fmt.Fprint(buf, fv(b))
+ }
+ }
+
+ if i < len(fields)-1 { // Skip space for last field
+ buf.WriteByte(' ')
+ }
+ }
+}
+
+// writePart appends a formatted part to buf.
+func (w ConsoleWriter) writePart(buf *bytes.Buffer, evt map[string]interface{}, p string) {
+ var f Formatter
+
+ if w.PartsExclude != nil && len(w.PartsExclude) > 0 {
+ for _, exclude := range w.PartsExclude {
+ if exclude == p {
+ return
+ }
+ }
+ }
+
+ switch p {
+ case LevelFieldName:
+ if w.FormatLevel == nil {
+ f = consoleDefaultFormatLevel(w.NoColor)
+ } else {
+ f = w.FormatLevel
+ }
+ case TimestampFieldName:
+ if w.FormatTimestamp == nil {
+ f = consoleDefaultFormatTimestamp(w.TimeFormat, w.TimeLocation, w.NoColor)
+ } else {
+ f = w.FormatTimestamp
+ }
+ case MessageFieldName:
+ if w.FormatMessage == nil {
+ f = consoleDefaultFormatMessage(w.NoColor, evt[LevelFieldName])
+ } else {
+ f = w.FormatMessage
+ }
+ case CallerFieldName:
+ if w.FormatCaller == nil {
+ f = consoleDefaultFormatCaller(w.NoColor)
+ } else {
+ f = w.FormatCaller
+ }
+ default:
+ if w.FormatFieldValue == nil {
+ f = consoleDefaultFormatFieldValue
+ } else {
+ f = w.FormatFieldValue
+ }
+ }
+
+ var s = f(evt[p])
+
+ if len(s) > 0 {
+ if buf.Len() > 0 {
+ buf.WriteByte(' ') // Write space only if not the first part
+ }
+ buf.WriteString(s)
+ }
+}
+
+// orderFields takes an array of field names and an array representing field order
+// and returns an array with any ordered fields at the beginning, in order,
+// and the remaining fields after in their original order.
+func (w ConsoleWriter) orderFields(fields []string) {
+ if w.fieldIsOrdered == nil {
+ w.fieldIsOrdered = make(map[string]int)
+ for i, fieldName := range w.FieldsOrder {
+ w.fieldIsOrdered[fieldName] = i
+ }
+ }
+ sort.Slice(fields, func(i, j int) bool {
+ ii, iOrdered := w.fieldIsOrdered[fields[i]]
+ jj, jOrdered := w.fieldIsOrdered[fields[j]]
+ if iOrdered && jOrdered {
+ return ii < jj
+ }
+ if iOrdered {
+ return true
+ }
+ if jOrdered {
+ return false
+ }
+ return fields[i] < fields[j]
+ })
+}
+
+// needsQuote returns true when the string s should be quoted in output.
+func needsQuote(s string) bool {
+ for i := range s {
+ if s[i] < 0x20 || s[i] > 0x7e || s[i] == ' ' || s[i] == '\\' || s[i] == '"' {
+ return true
+ }
+ }
+ return false
+}
+
+// colorize returns the string s wrapped in ANSI code c, unless disabled is true or c is 0.
+func colorize(s interface{}, c int, disabled bool) string {
+ e := os.Getenv("NO_COLOR")
+ if e != "" || c == 0 {
+ disabled = true
+ }
+
+ if disabled {
+ return fmt.Sprintf("%s", s)
+ }
+ return fmt.Sprintf("\x1b[%dm%v\x1b[0m", c, s)
+}
+
+// ----- DEFAULT FORMATTERS ---------------------------------------------------
+
+func consoleDefaultPartsOrder() []string {
+ return []string{
+ TimestampFieldName,
+ LevelFieldName,
+ CallerFieldName,
+ MessageFieldName,
+ }
+}
+
+func consoleDefaultFormatTimestamp(timeFormat string, location *time.Location, noColor bool) Formatter {
+ if timeFormat == "" {
+ timeFormat = consoleDefaultTimeFormat
+ }
+ if location == nil {
+ location = time.Local
+ }
+
+ return func(i interface{}) string {
+ t := ""
+ switch tt := i.(type) {
+ case string:
+ ts, err := time.ParseInLocation(TimeFieldFormat, tt, location)
+ if err != nil {
+ t = tt
+ } else {
+ t = ts.In(location).Format(timeFormat)
+ }
+ case json.Number:
+ i, err := tt.Int64()
+ if err != nil {
+ t = tt.String()
+ } else {
+ var sec, nsec int64
+
+ switch TimeFieldFormat {
+ case TimeFormatUnixNano:
+ sec, nsec = 0, i
+ case TimeFormatUnixMicro:
+ sec, nsec = 0, int64(time.Duration(i)*time.Microsecond)
+ case TimeFormatUnixMs:
+ sec, nsec = 0, int64(time.Duration(i)*time.Millisecond)
+ default:
+ sec, nsec = i, 0
+ }
+
+ ts := time.Unix(sec, nsec)
+ t = ts.In(location).Format(timeFormat)
+ }
+ }
+ return colorize(t, colorDarkGray, noColor)
+ }
+}
+
+func stripLevel(ll string) string {
+ if len(ll) == 0 {
+ return unknownLevel
+ }
+ if len(ll) > 3 {
+ ll = ll[:3]
+ }
+ return strings.ToUpper(ll)
+}
+
+func consoleDefaultFormatLevel(noColor bool) Formatter {
+ return func(i interface{}) string {
+ if ll, ok := i.(string); ok {
+ level, _ := ParseLevel(ll)
+ fl, ok := FormattedLevels[level]
+ if ok {
+ return colorize(fl, LevelColors[level], noColor)
+ }
+ return stripLevel(ll)
+ }
+ if i == nil {
+ return unknownLevel
+ }
+ return stripLevel(fmt.Sprintf("%s", i))
+ }
+}
+
+func consoleDefaultFormatCaller(noColor bool) Formatter {
+ return func(i interface{}) string {
+ var c string
+ if cc, ok := i.(string); ok {
+ c = cc
+ }
+ if len(c) > 0 {
+ if cwd, err := os.Getwd(); err == nil {
+ if rel, err := filepath.Rel(cwd, c); err == nil {
+ c = rel
+ }
+ }
+ c = colorize(c, colorBold, noColor) + colorize(" >", colorCyan, noColor)
+ }
+ return c
+ }
+}
+
+func consoleDefaultFormatMessage(noColor bool, level interface{}) Formatter {
+ return func(i interface{}) string {
+ if i == nil || i == "" {
+ return ""
+ }
+ switch level {
+ case LevelInfoValue, LevelWarnValue, LevelErrorValue, LevelFatalValue, LevelPanicValue:
+ return colorize(fmt.Sprintf("%s", i), colorBold, noColor)
+ default:
+ return fmt.Sprintf("%s", i)
+ }
+ }
+}
+
+func consoleDefaultFormatFieldName(noColor bool) Formatter {
+ return func(i interface{}) string {
+ return colorize(fmt.Sprintf("%s=", i), colorCyan, noColor)
+ }
+}
+
+func consoleDefaultFormatFieldValue(i interface{}) string {
+ return fmt.Sprintf("%s", i)
+}
+
+func consoleDefaultFormatErrFieldName(noColor bool) Formatter {
+ return func(i interface{}) string {
+ return colorize(fmt.Sprintf("%s=", i), colorCyan, noColor)
+ }
+}
+
+func consoleDefaultFormatErrFieldValue(noColor bool) Formatter {
+ return func(i interface{}) string {
+ return colorize(colorize(fmt.Sprintf("%s", i), colorBold, noColor), colorRed, noColor)
+ }
+}
diff --git a/vendor/github.com/rs/zerolog/context.go b/vendor/github.com/rs/zerolog/context.go
new file mode 100644
index 00000000..ff9a3ae2
--- /dev/null
+++ b/vendor/github.com/rs/zerolog/context.go
@@ -0,0 +1,480 @@
+package zerolog
+
+import (
+ "context"
+ "fmt"
+ "io"
+ "math"
+ "net"
+ "time"
+)
+
+// Context configures a new sub-logger with contextual fields.
+type Context struct {
+ l Logger
+}
+
+// Logger returns the logger with the context previously set.
+func (c Context) Logger() Logger {
+ return c.l
+}
+
+// Fields is a helper function to use a map or slice to set fields using type assertion.
+// Only map[string]interface{} and []interface{} are accepted. []interface{} must
+// alternate string keys and arbitrary values, and extraneous ones are ignored.
+func (c Context) Fields(fields interface{}) Context {
+ c.l.context = appendFields(c.l.context, fields, c.l.stack)
+ return c
+}
+
+// Dict adds the field key with the dict to the logger context.
+func (c Context) Dict(key string, dict *Event) Context {
+ dict.buf = enc.AppendEndMarker(dict.buf)
+ c.l.context = append(enc.AppendKey(c.l.context, key), dict.buf...)
+ putEvent(dict)
+ return c
+}
+
+// Array adds the field key with an array to the event context.
+// Use zerolog.Arr() to create the array or pass a type that
+// implement the LogArrayMarshaler interface.
+func (c Context) Array(key string, arr LogArrayMarshaler) Context {
+ c.l.context = enc.AppendKey(c.l.context, key)
+ if arr, ok := arr.(*Array); ok {
+ c.l.context = arr.write(c.l.context)
+ return c
+ }
+ var a *Array
+ if aa, ok := arr.(*Array); ok {
+ a = aa
+ } else {
+ a = Arr()
+ arr.MarshalZerologArray(a)
+ }
+ c.l.context = a.write(c.l.context)
+ return c
+}
+
+// Object marshals an object that implement the LogObjectMarshaler interface.
+func (c Context) Object(key string, obj LogObjectMarshaler) Context {
+ e := newEvent(LevelWriterAdapter{io.Discard}, 0)
+ e.Object(key, obj)
+ c.l.context = enc.AppendObjectData(c.l.context, e.buf)
+ putEvent(e)
+ return c
+}
+
+// EmbedObject marshals and Embeds an object that implement the LogObjectMarshaler interface.
+func (c Context) EmbedObject(obj LogObjectMarshaler) Context {
+ e := newEvent(LevelWriterAdapter{io.Discard}, 0)
+ e.EmbedObject(obj)
+ c.l.context = enc.AppendObjectData(c.l.context, e.buf)
+ putEvent(e)
+ return c
+}
+
+// Str adds the field key with val as a string to the logger context.
+func (c Context) Str(key, val string) Context {
+ c.l.context = enc.AppendString(enc.AppendKey(c.l.context, key), val)
+ return c
+}
+
+// Strs adds the field key with val as a string to the logger context.
+func (c Context) Strs(key string, vals []string) Context {
+ c.l.context = enc.AppendStrings(enc.AppendKey(c.l.context, key), vals)
+ return c
+}
+
+// Stringer adds the field key with val.String() (or null if val is nil) to the logger context.
+func (c Context) Stringer(key string, val fmt.Stringer) Context {
+ if val != nil {
+ c.l.context = enc.AppendString(enc.AppendKey(c.l.context, key), val.String())
+ return c
+ }
+
+ c.l.context = enc.AppendInterface(enc.AppendKey(c.l.context, key), nil)
+ return c
+}
+
+// Bytes adds the field key with val as a []byte to the logger context.
+func (c Context) Bytes(key string, val []byte) Context {
+ c.l.context = enc.AppendBytes(enc.AppendKey(c.l.context, key), val)
+ return c
+}
+
+// Hex adds the field key with val as a hex string to the logger context.
+func (c Context) Hex(key string, val []byte) Context {
+ c.l.context = enc.AppendHex(enc.AppendKey(c.l.context, key), val)
+ return c
+}
+
+// RawJSON adds already encoded JSON to context.
+//
+// No sanity check is performed on b; it must not contain carriage returns and
+// be valid JSON.
+func (c Context) RawJSON(key string, b []byte) Context {
+ c.l.context = appendJSON(enc.AppendKey(c.l.context, key), b)
+ return c
+}
+
+// AnErr adds the field key with serialized err to the logger context.
+func (c Context) AnErr(key string, err error) Context {
+ switch m := ErrorMarshalFunc(err).(type) {
+ case nil:
+ return c
+ case LogObjectMarshaler:
+ return c.Object(key, m)
+ case error:
+ if m == nil || isNilValue(m) {
+ return c
+ } else {
+ return c.Str(key, m.Error())
+ }
+ case string:
+ return c.Str(key, m)
+ default:
+ return c.Interface(key, m)
+ }
+}
+
+// Errs adds the field key with errs as an array of serialized errors to the
+// logger context.
+func (c Context) Errs(key string, errs []error) Context {
+ arr := Arr()
+ for _, err := range errs {
+ switch m := ErrorMarshalFunc(err).(type) {
+ case LogObjectMarshaler:
+ arr = arr.Object(m)
+ case error:
+ if m == nil || isNilValue(m) {
+ arr = arr.Interface(nil)
+ } else {
+ arr = arr.Str(m.Error())
+ }
+ case string:
+ arr = arr.Str(m)
+ default:
+ arr = arr.Interface(m)
+ }
+ }
+
+ return c.Array(key, arr)
+}
+
+// Err adds the field "error" with serialized err to the logger context.
+func (c Context) Err(err error) Context {
+ if c.l.stack && ErrorStackMarshaler != nil {
+ switch m := ErrorStackMarshaler(err).(type) {
+ case nil:
+ case LogObjectMarshaler:
+ c = c.Object(ErrorStackFieldName, m)
+ case error:
+ if m != nil && !isNilValue(m) {
+ c = c.Str(ErrorStackFieldName, m.Error())
+ }
+ case string:
+ c = c.Str(ErrorStackFieldName, m)
+ default:
+ c = c.Interface(ErrorStackFieldName, m)
+ }
+ }
+
+ return c.AnErr(ErrorFieldName, err)
+}
+
+// Ctx adds the context.Context to the logger context. The context.Context is
+// not rendered in the error message, but is made available for hooks to use.
+// A typical use case is to extract tracing information from the
+// context.Context.
+func (c Context) Ctx(ctx context.Context) Context {
+ c.l.ctx = ctx
+ return c
+}
+
+// Bool adds the field key with val as a bool to the logger context.
+func (c Context) Bool(key string, b bool) Context {
+ c.l.context = enc.AppendBool(enc.AppendKey(c.l.context, key), b)
+ return c
+}
+
+// Bools adds the field key with val as a []bool to the logger context.
+func (c Context) Bools(key string, b []bool) Context {
+ c.l.context = enc.AppendBools(enc.AppendKey(c.l.context, key), b)
+ return c
+}
+
+// Int adds the field key with i as a int to the logger context.
+func (c Context) Int(key string, i int) Context {
+ c.l.context = enc.AppendInt(enc.AppendKey(c.l.context, key), i)
+ return c
+}
+
+// Ints adds the field key with i as a []int to the logger context.
+func (c Context) Ints(key string, i []int) Context {
+ c.l.context = enc.AppendInts(enc.AppendKey(c.l.context, key), i)
+ return c
+}
+
+// Int8 adds the field key with i as a int8 to the logger context.
+func (c Context) Int8(key string, i int8) Context {
+ c.l.context = enc.AppendInt8(enc.AppendKey(c.l.context, key), i)
+ return c
+}
+
+// Ints8 adds the field key with i as a []int8 to the logger context.
+func (c Context) Ints8(key string, i []int8) Context {
+ c.l.context = enc.AppendInts8(enc.AppendKey(c.l.context, key), i)
+ return c
+}
+
+// Int16 adds the field key with i as a int16 to the logger context.
+func (c Context) Int16(key string, i int16) Context {
+ c.l.context = enc.AppendInt16(enc.AppendKey(c.l.context, key), i)
+ return c
+}
+
+// Ints16 adds the field key with i as a []int16 to the logger context.
+func (c Context) Ints16(key string, i []int16) Context {
+ c.l.context = enc.AppendInts16(enc.AppendKey(c.l.context, key), i)
+ return c
+}
+
+// Int32 adds the field key with i as a int32 to the logger context.
+func (c Context) Int32(key string, i int32) Context {
+ c.l.context = enc.AppendInt32(enc.AppendKey(c.l.context, key), i)
+ return c
+}
+
+// Ints32 adds the field key with i as a []int32 to the logger context.
+func (c Context) Ints32(key string, i []int32) Context {
+ c.l.context = enc.AppendInts32(enc.AppendKey(c.l.context, key), i)
+ return c
+}
+
+// Int64 adds the field key with i as a int64 to the logger context.
+func (c Context) Int64(key string, i int64) Context {
+ c.l.context = enc.AppendInt64(enc.AppendKey(c.l.context, key), i)
+ return c
+}
+
+// Ints64 adds the field key with i as a []int64 to the logger context.
+func (c Context) Ints64(key string, i []int64) Context {
+ c.l.context = enc.AppendInts64(enc.AppendKey(c.l.context, key), i)
+ return c
+}
+
+// Uint adds the field key with i as a uint to the logger context.
+func (c Context) Uint(key string, i uint) Context {
+ c.l.context = enc.AppendUint(enc.AppendKey(c.l.context, key), i)
+ return c
+}
+
+// Uints adds the field key with i as a []uint to the logger context.
+func (c Context) Uints(key string, i []uint) Context {
+ c.l.context = enc.AppendUints(enc.AppendKey(c.l.context, key), i)
+ return c
+}
+
+// Uint8 adds the field key with i as a uint8 to the logger context.
+func (c Context) Uint8(key string, i uint8) Context {
+ c.l.context = enc.AppendUint8(enc.AppendKey(c.l.context, key), i)
+ return c
+}
+
+// Uints8 adds the field key with i as a []uint8 to the logger context.
+func (c Context) Uints8(key string, i []uint8) Context {
+ c.l.context = enc.AppendUints8(enc.AppendKey(c.l.context, key), i)
+ return c
+}
+
+// Uint16 adds the field key with i as a uint16 to the logger context.
+func (c Context) Uint16(key string, i uint16) Context {
+ c.l.context = enc.AppendUint16(enc.AppendKey(c.l.context, key), i)
+ return c
+}
+
+// Uints16 adds the field key with i as a []uint16 to the logger context.
+func (c Context) Uints16(key string, i []uint16) Context {
+ c.l.context = enc.AppendUints16(enc.AppendKey(c.l.context, key), i)
+ return c
+}
+
+// Uint32 adds the field key with i as a uint32 to the logger context.
+func (c Context) Uint32(key string, i uint32) Context {
+ c.l.context = enc.AppendUint32(enc.AppendKey(c.l.context, key), i)
+ return c
+}
+
+// Uints32 adds the field key with i as a []uint32 to the logger context.
+func (c Context) Uints32(key string, i []uint32) Context {
+ c.l.context = enc.AppendUints32(enc.AppendKey(c.l.context, key), i)
+ return c
+}
+
+// Uint64 adds the field key with i as a uint64 to the logger context.
+func (c Context) Uint64(key string, i uint64) Context {
+ c.l.context = enc.AppendUint64(enc.AppendKey(c.l.context, key), i)
+ return c
+}
+
+// Uints64 adds the field key with i as a []uint64 to the logger context.
+func (c Context) Uints64(key string, i []uint64) Context {
+ c.l.context = enc.AppendUints64(enc.AppendKey(c.l.context, key), i)
+ return c
+}
+
+// Float32 adds the field key with f as a float32 to the logger context.
+func (c Context) Float32(key string, f float32) Context {
+ c.l.context = enc.AppendFloat32(enc.AppendKey(c.l.context, key), f, FloatingPointPrecision)
+ return c
+}
+
+// Floats32 adds the field key with f as a []float32 to the logger context.
+func (c Context) Floats32(key string, f []float32) Context {
+ c.l.context = enc.AppendFloats32(enc.AppendKey(c.l.context, key), f, FloatingPointPrecision)
+ return c
+}
+
+// Float64 adds the field key with f as a float64 to the logger context.
+func (c Context) Float64(key string, f float64) Context {
+ c.l.context = enc.AppendFloat64(enc.AppendKey(c.l.context, key), f, FloatingPointPrecision)
+ return c
+}
+
+// Floats64 adds the field key with f as a []float64 to the logger context.
+func (c Context) Floats64(key string, f []float64) Context {
+ c.l.context = enc.AppendFloats64(enc.AppendKey(c.l.context, key), f, FloatingPointPrecision)
+ return c
+}
+
+type timestampHook struct{}
+
+func (ts timestampHook) Run(e *Event, level Level, msg string) {
+ e.Timestamp()
+}
+
+var th = timestampHook{}
+
+// Timestamp adds the current local time to the logger context with the "time" key, formatted using zerolog.TimeFieldFormat.
+// To customize the key name, change zerolog.TimestampFieldName.
+// To customize the time format, change zerolog.TimeFieldFormat.
+//
+// NOTE: It won't dedupe the "time" key if the *Context has one already.
+func (c Context) Timestamp() Context {
+ c.l = c.l.Hook(th)
+ return c
+}
+
+// Time adds the field key with t formatted as string using zerolog.TimeFieldFormat.
+func (c Context) Time(key string, t time.Time) Context {
+ c.l.context = enc.AppendTime(enc.AppendKey(c.l.context, key), t, TimeFieldFormat)
+ return c
+}
+
+// Times adds the field key with t formatted as string using zerolog.TimeFieldFormat.
+func (c Context) Times(key string, t []time.Time) Context {
+ c.l.context = enc.AppendTimes(enc.AppendKey(c.l.context, key), t, TimeFieldFormat)
+ return c
+}
+
+// Dur adds the fields key with d divided by unit and stored as a float.
+func (c Context) Dur(key string, d time.Duration) Context {
+ c.l.context = enc.AppendDuration(enc.AppendKey(c.l.context, key), d, DurationFieldUnit, DurationFieldInteger, FloatingPointPrecision)
+ return c
+}
+
+// Durs adds the fields key with d divided by unit and stored as a float.
+func (c Context) Durs(key string, d []time.Duration) Context {
+ c.l.context = enc.AppendDurations(enc.AppendKey(c.l.context, key), d, DurationFieldUnit, DurationFieldInteger, FloatingPointPrecision)
+ return c
+}
+
+// Interface adds the field key with obj marshaled using reflection.
+func (c Context) Interface(key string, i interface{}) Context {
+ if obj, ok := i.(LogObjectMarshaler); ok {
+ return c.Object(key, obj)
+ }
+ c.l.context = enc.AppendInterface(enc.AppendKey(c.l.context, key), i)
+ return c
+}
+
+// Type adds the field key with val's type using reflection.
+func (c Context) Type(key string, val interface{}) Context {
+ c.l.context = enc.AppendType(enc.AppendKey(c.l.context, key), val)
+ return c
+}
+
+// Any is a wrapper around Context.Interface.
+func (c Context) Any(key string, i interface{}) Context {
+ return c.Interface(key, i)
+}
+
+// Reset removes all the context fields.
+func (c Context) Reset() Context {
+ c.l.context = enc.AppendBeginMarker(make([]byte, 0, 500))
+ return c
+}
+
+type callerHook struct {
+ callerSkipFrameCount int
+}
+
+func newCallerHook(skipFrameCount int) callerHook {
+ return callerHook{callerSkipFrameCount: skipFrameCount}
+}
+
+func (ch callerHook) Run(e *Event, level Level, msg string) {
+ switch ch.callerSkipFrameCount {
+ case useGlobalSkipFrameCount:
+ // Extra frames to skip (added by hook infra).
+ e.caller(CallerSkipFrameCount + contextCallerSkipFrameCount)
+ default:
+ // Extra frames to skip (added by hook infra).
+ e.caller(ch.callerSkipFrameCount + contextCallerSkipFrameCount)
+ }
+}
+
+// useGlobalSkipFrameCount acts as a flag to informat callerHook.Run
+// to use the global CallerSkipFrameCount.
+const useGlobalSkipFrameCount = math.MinInt32
+
+// ch is the default caller hook using the global CallerSkipFrameCount.
+var ch = newCallerHook(useGlobalSkipFrameCount)
+
+// Caller adds the file:line of the caller with the zerolog.CallerFieldName key.
+func (c Context) Caller() Context {
+ c.l = c.l.Hook(ch)
+ return c
+}
+
+// CallerWithSkipFrameCount adds the file:line of the caller with the zerolog.CallerFieldName key.
+// The specified skipFrameCount int will override the global CallerSkipFrameCount for this context's respective logger.
+// If set to -1 the global CallerSkipFrameCount will be used.
+func (c Context) CallerWithSkipFrameCount(skipFrameCount int) Context {
+ c.l = c.l.Hook(newCallerHook(skipFrameCount))
+ return c
+}
+
+// Stack enables stack trace printing for the error passed to Err().
+func (c Context) Stack() Context {
+ c.l.stack = true
+ return c
+}
+
+// IPAddr adds IPv4 or IPv6 Address to the context
+func (c Context) IPAddr(key string, ip net.IP) Context {
+ c.l.context = enc.AppendIPAddr(enc.AppendKey(c.l.context, key), ip)
+ return c
+}
+
+// IPPrefix adds IPv4 or IPv6 Prefix (address and mask) to the context
+func (c Context) IPPrefix(key string, pfx net.IPNet) Context {
+ c.l.context = enc.AppendIPPrefix(enc.AppendKey(c.l.context, key), pfx)
+ return c
+}
+
+// MACAddr adds MAC address to the context
+func (c Context) MACAddr(key string, ha net.HardwareAddr) Context {
+ c.l.context = enc.AppendMACAddr(enc.AppendKey(c.l.context, key), ha)
+ return c
+}
diff --git a/vendor/github.com/rs/zerolog/ctx.go b/vendor/github.com/rs/zerolog/ctx.go
new file mode 100644
index 00000000..60432d15
--- /dev/null
+++ b/vendor/github.com/rs/zerolog/ctx.go
@@ -0,0 +1,52 @@
+package zerolog
+
+import (
+ "context"
+)
+
+var disabledLogger *Logger
+
+func init() {
+ SetGlobalLevel(TraceLevel)
+ l := Nop()
+ disabledLogger = &l
+}
+
+type ctxKey struct{}
+
+// WithContext returns a copy of ctx with the receiver attached. The Logger
+// attached to the provided Context (if any) will not be effected. If the
+// receiver's log level is Disabled it will only be attached to the returned
+// Context if the provided Context has a previously attached Logger. If the
+// provided Context has no attached Logger, a Disabled Logger will not be
+// attached.
+//
+// Note: to modify the existing Logger attached to a Context (instead of
+// replacing it in a new Context), use UpdateContext with the following
+// notation:
+//
+// ctx := r.Context()
+// l := zerolog.Ctx(ctx)
+// l.UpdateContext(func(c Context) Context {
+// return c.Str("bar", "baz")
+// })
+//
+func (l Logger) WithContext(ctx context.Context) context.Context {
+ if _, ok := ctx.Value(ctxKey{}).(*Logger); !ok && l.level == Disabled {
+ // Do not store disabled logger.
+ return ctx
+ }
+ return context.WithValue(ctx, ctxKey{}, &l)
+}
+
+// Ctx returns the Logger associated with the ctx. If no logger
+// is associated, DefaultContextLogger is returned, unless DefaultContextLogger
+// is nil, in which case a disabled logger is returned.
+func Ctx(ctx context.Context) *Logger {
+ if l, ok := ctx.Value(ctxKey{}).(*Logger); ok {
+ return l
+ } else if l = DefaultContextLogger; l != nil {
+ return l
+ }
+ return disabledLogger
+}
diff --git a/vendor/github.com/rs/zerolog/encoder.go b/vendor/github.com/rs/zerolog/encoder.go
new file mode 100644
index 00000000..4dbaf380
--- /dev/null
+++ b/vendor/github.com/rs/zerolog/encoder.go
@@ -0,0 +1,56 @@
+package zerolog
+
+import (
+ "net"
+ "time"
+)
+
+type encoder interface {
+ AppendArrayDelim(dst []byte) []byte
+ AppendArrayEnd(dst []byte) []byte
+ AppendArrayStart(dst []byte) []byte
+ AppendBeginMarker(dst []byte) []byte
+ AppendBool(dst []byte, val bool) []byte
+ AppendBools(dst []byte, vals []bool) []byte
+ AppendBytes(dst, s []byte) []byte
+ AppendDuration(dst []byte, d time.Duration, unit time.Duration, useInt bool, precision int) []byte
+ AppendDurations(dst []byte, vals []time.Duration, unit time.Duration, useInt bool, precision int) []byte
+ AppendEndMarker(dst []byte) []byte
+ AppendFloat32(dst []byte, val float32, precision int) []byte
+ AppendFloat64(dst []byte, val float64, precision int) []byte
+ AppendFloats32(dst []byte, vals []float32, precision int) []byte
+ AppendFloats64(dst []byte, vals []float64, precision int) []byte
+ AppendHex(dst, s []byte) []byte
+ AppendIPAddr(dst []byte, ip net.IP) []byte
+ AppendIPPrefix(dst []byte, pfx net.IPNet) []byte
+ AppendInt(dst []byte, val int) []byte
+ AppendInt16(dst []byte, val int16) []byte
+ AppendInt32(dst []byte, val int32) []byte
+ AppendInt64(dst []byte, val int64) []byte
+ AppendInt8(dst []byte, val int8) []byte
+ AppendInterface(dst []byte, i interface{}) []byte
+ AppendInts(dst []byte, vals []int) []byte
+ AppendInts16(dst []byte, vals []int16) []byte
+ AppendInts32(dst []byte, vals []int32) []byte
+ AppendInts64(dst []byte, vals []int64) []byte
+ AppendInts8(dst []byte, vals []int8) []byte
+ AppendKey(dst []byte, key string) []byte
+ AppendLineBreak(dst []byte) []byte
+ AppendMACAddr(dst []byte, ha net.HardwareAddr) []byte
+ AppendNil(dst []byte) []byte
+ AppendObjectData(dst []byte, o []byte) []byte
+ AppendString(dst []byte, s string) []byte
+ AppendStrings(dst []byte, vals []string) []byte
+ AppendTime(dst []byte, t time.Time, format string) []byte
+ AppendTimes(dst []byte, vals []time.Time, format string) []byte
+ AppendUint(dst []byte, val uint) []byte
+ AppendUint16(dst []byte, val uint16) []byte
+ AppendUint32(dst []byte, val uint32) []byte
+ AppendUint64(dst []byte, val uint64) []byte
+ AppendUint8(dst []byte, val uint8) []byte
+ AppendUints(dst []byte, vals []uint) []byte
+ AppendUints16(dst []byte, vals []uint16) []byte
+ AppendUints32(dst []byte, vals []uint32) []byte
+ AppendUints64(dst []byte, vals []uint64) []byte
+ AppendUints8(dst []byte, vals []uint8) []byte
+}
diff --git a/vendor/github.com/rs/zerolog/encoder_cbor.go b/vendor/github.com/rs/zerolog/encoder_cbor.go
new file mode 100644
index 00000000..36cb994b
--- /dev/null
+++ b/vendor/github.com/rs/zerolog/encoder_cbor.go
@@ -0,0 +1,45 @@
+// +build binary_log
+
+package zerolog
+
+// This file contains bindings to do binary encoding.
+
+import (
+ "github.com/rs/zerolog/internal/cbor"
+)
+
+var (
+ _ encoder = (*cbor.Encoder)(nil)
+
+ enc = cbor.Encoder{}
+)
+
+func init() {
+ // using closure to reflect the changes at runtime.
+ cbor.JSONMarshalFunc = func(v interface{}) ([]byte, error) {
+ return InterfaceMarshalFunc(v)
+ }
+}
+
+func appendJSON(dst []byte, j []byte) []byte {
+ return cbor.AppendEmbeddedJSON(dst, j)
+}
+func appendCBOR(dst []byte, c []byte) []byte {
+ return cbor.AppendEmbeddedCBOR(dst, c)
+}
+
+// decodeIfBinaryToString - converts a binary formatted log msg to a
+// JSON formatted String Log message.
+func decodeIfBinaryToString(in []byte) string {
+ return cbor.DecodeIfBinaryToString(in)
+}
+
+func decodeObjectToStr(in []byte) string {
+ return cbor.DecodeObjectToStr(in)
+}
+
+// decodeIfBinaryToBytes - converts a binary formatted log msg to a
+// JSON formatted Bytes Log message.
+func decodeIfBinaryToBytes(in []byte) []byte {
+ return cbor.DecodeIfBinaryToBytes(in)
+}
diff --git a/vendor/github.com/rs/zerolog/encoder_json.go b/vendor/github.com/rs/zerolog/encoder_json.go
new file mode 100644
index 00000000..6f96c68a
--- /dev/null
+++ b/vendor/github.com/rs/zerolog/encoder_json.go
@@ -0,0 +1,51 @@
+// +build !binary_log
+
+package zerolog
+
+// encoder_json.go file contains bindings to generate
+// JSON encoded byte stream.
+
+import (
+ "encoding/base64"
+ "github.com/rs/zerolog/internal/json"
+)
+
+var (
+ _ encoder = (*json.Encoder)(nil)
+
+ enc = json.Encoder{}
+)
+
+func init() {
+ // using closure to reflect the changes at runtime.
+ json.JSONMarshalFunc = func(v interface{}) ([]byte, error) {
+ return InterfaceMarshalFunc(v)
+ }
+}
+
+func appendJSON(dst []byte, j []byte) []byte {
+ return append(dst, j...)
+}
+func appendCBOR(dst []byte, cbor []byte) []byte {
+ dst = append(dst, []byte("\"data:application/cbor;base64,")...)
+ l := len(dst)
+ enc := base64.StdEncoding
+ n := enc.EncodedLen(len(cbor))
+ for i := 0; i < n; i++ {
+ dst = append(dst, '.')
+ }
+ enc.Encode(dst[l:], cbor)
+ return append(dst, '"')
+}
+
+func decodeIfBinaryToString(in []byte) string {
+ return string(in)
+}
+
+func decodeObjectToStr(in []byte) string {
+ return string(in)
+}
+
+func decodeIfBinaryToBytes(in []byte) []byte {
+ return in
+}
diff --git a/vendor/github.com/rs/zerolog/event.go b/vendor/github.com/rs/zerolog/event.go
new file mode 100644
index 00000000..56de6061
--- /dev/null
+++ b/vendor/github.com/rs/zerolog/event.go
@@ -0,0 +1,830 @@
+package zerolog
+
+import (
+ "context"
+ "fmt"
+ "net"
+ "os"
+ "runtime"
+ "sync"
+ "time"
+)
+
+var eventPool = &sync.Pool{
+ New: func() interface{} {
+ return &Event{
+ buf: make([]byte, 0, 500),
+ }
+ },
+}
+
+// Event represents a log event. It is instanced by one of the level method of
+// Logger and finalized by the Msg or Msgf method.
+type Event struct {
+ buf []byte
+ w LevelWriter
+ level Level
+ done func(msg string)
+ stack bool // enable error stack trace
+ ch []Hook // hooks from context
+ skipFrame int // The number of additional frames to skip when printing the caller.
+ ctx context.Context // Optional Go context for event
+}
+
+func putEvent(e *Event) {
+ // Proper usage of a sync.Pool requires each entry to have approximately
+ // the same memory cost. To obtain this property when the stored type
+ // contains a variably-sized buffer, we add a hard limit on the maximum buffer
+ // to place back in the pool.
+ //
+ // See https://golang.org/issue/23199
+ const maxSize = 1 << 16 // 64KiB
+ if cap(e.buf) > maxSize {
+ return
+ }
+ eventPool.Put(e)
+}
+
+// LogObjectMarshaler provides a strongly-typed and encoding-agnostic interface
+// to be implemented by types used with Event/Context's Object methods.
+type LogObjectMarshaler interface {
+ MarshalZerologObject(e *Event)
+}
+
+// LogArrayMarshaler provides a strongly-typed and encoding-agnostic interface
+// to be implemented by types used with Event/Context's Array methods.
+type LogArrayMarshaler interface {
+ MarshalZerologArray(a *Array)
+}
+
+func newEvent(w LevelWriter, level Level) *Event {
+ e := eventPool.Get().(*Event)
+ e.buf = e.buf[:0]
+ e.ch = nil
+ e.buf = enc.AppendBeginMarker(e.buf)
+ e.w = w
+ e.level = level
+ e.stack = false
+ e.skipFrame = 0
+ return e
+}
+
+func (e *Event) write() (err error) {
+ if e == nil {
+ return nil
+ }
+ if e.level != Disabled {
+ e.buf = enc.AppendEndMarker(e.buf)
+ e.buf = enc.AppendLineBreak(e.buf)
+ if e.w != nil {
+ _, err = e.w.WriteLevel(e.level, e.buf)
+ }
+ }
+ putEvent(e)
+ return
+}
+
+// Enabled return false if the *Event is going to be filtered out by
+// log level or sampling.
+func (e *Event) Enabled() bool {
+ return e != nil && e.level != Disabled
+}
+
+// Discard disables the event so Msg(f) won't print it.
+func (e *Event) Discard() *Event {
+ if e == nil {
+ return e
+ }
+ e.level = Disabled
+ return nil
+}
+
+// Msg sends the *Event with msg added as the message field if not empty.
+//
+// NOTICE: once this method is called, the *Event should be disposed.
+// Calling Msg twice can have unexpected result.
+func (e *Event) Msg(msg string) {
+ if e == nil {
+ return
+ }
+ e.msg(msg)
+}
+
+// Send is equivalent to calling Msg("").
+//
+// NOTICE: once this method is called, the *Event should be disposed.
+func (e *Event) Send() {
+ if e == nil {
+ return
+ }
+ e.msg("")
+}
+
+// Msgf sends the event with formatted msg added as the message field if not empty.
+//
+// NOTICE: once this method is called, the *Event should be disposed.
+// Calling Msgf twice can have unexpected result.
+func (e *Event) Msgf(format string, v ...interface{}) {
+ if e == nil {
+ return
+ }
+ e.msg(fmt.Sprintf(format, v...))
+}
+
+func (e *Event) MsgFunc(createMsg func() string) {
+ if e == nil {
+ return
+ }
+ e.msg(createMsg())
+}
+
+func (e *Event) msg(msg string) {
+ for _, hook := range e.ch {
+ hook.Run(e, e.level, msg)
+ }
+ if msg != "" {
+ e.buf = enc.AppendString(enc.AppendKey(e.buf, MessageFieldName), msg)
+ }
+ if e.done != nil {
+ defer e.done(msg)
+ }
+ if err := e.write(); err != nil {
+ if ErrorHandler != nil {
+ ErrorHandler(err)
+ } else {
+ fmt.Fprintf(os.Stderr, "zerolog: could not write event: %v\n", err)
+ }
+ }
+}
+
+// Fields is a helper function to use a map or slice to set fields using type assertion.
+// Only map[string]interface{} and []interface{} are accepted. []interface{} must
+// alternate string keys and arbitrary values, and extraneous ones are ignored.
+func (e *Event) Fields(fields interface{}) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = appendFields(e.buf, fields, e.stack)
+ return e
+}
+
+// Dict adds the field key with a dict to the event context.
+// Use zerolog.Dict() to create the dictionary.
+func (e *Event) Dict(key string, dict *Event) *Event {
+ if e == nil {
+ return e
+ }
+ dict.buf = enc.AppendEndMarker(dict.buf)
+ e.buf = append(enc.AppendKey(e.buf, key), dict.buf...)
+ putEvent(dict)
+ return e
+}
+
+// Dict creates an Event to be used with the *Event.Dict method.
+// Call usual field methods like Str, Int etc to add fields to this
+// event and give it as argument the *Event.Dict method.
+func Dict() *Event {
+ return newEvent(nil, 0)
+}
+
+// Array adds the field key with an array to the event context.
+// Use zerolog.Arr() to create the array or pass a type that
+// implement the LogArrayMarshaler interface.
+func (e *Event) Array(key string, arr LogArrayMarshaler) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = enc.AppendKey(e.buf, key)
+ var a *Array
+ if aa, ok := arr.(*Array); ok {
+ a = aa
+ } else {
+ a = Arr()
+ arr.MarshalZerologArray(a)
+ }
+ e.buf = a.write(e.buf)
+ return e
+}
+
+func (e *Event) appendObject(obj LogObjectMarshaler) {
+ e.buf = enc.AppendBeginMarker(e.buf)
+ obj.MarshalZerologObject(e)
+ e.buf = enc.AppendEndMarker(e.buf)
+}
+
+// Object marshals an object that implement the LogObjectMarshaler interface.
+func (e *Event) Object(key string, obj LogObjectMarshaler) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = enc.AppendKey(e.buf, key)
+ if obj == nil {
+ e.buf = enc.AppendNil(e.buf)
+
+ return e
+ }
+
+ e.appendObject(obj)
+ return e
+}
+
+// Func allows an anonymous func to run only if the event is enabled.
+func (e *Event) Func(f func(e *Event)) *Event {
+ if e != nil && e.Enabled() {
+ f(e)
+ }
+ return e
+}
+
+// EmbedObject marshals an object that implement the LogObjectMarshaler interface.
+func (e *Event) EmbedObject(obj LogObjectMarshaler) *Event {
+ if e == nil {
+ return e
+ }
+ if obj == nil {
+ return e
+ }
+ obj.MarshalZerologObject(e)
+ return e
+}
+
+// Str adds the field key with val as a string to the *Event context.
+func (e *Event) Str(key, val string) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = enc.AppendString(enc.AppendKey(e.buf, key), val)
+ return e
+}
+
+// Strs adds the field key with vals as a []string to the *Event context.
+func (e *Event) Strs(key string, vals []string) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = enc.AppendStrings(enc.AppendKey(e.buf, key), vals)
+ return e
+}
+
+// Stringer adds the field key with val.String() (or null if val is nil)
+// to the *Event context.
+func (e *Event) Stringer(key string, val fmt.Stringer) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = enc.AppendStringer(enc.AppendKey(e.buf, key), val)
+ return e
+}
+
+// Stringers adds the field key with vals where each individual val
+// is used as val.String() (or null if val is empty) to the *Event
+// context.
+func (e *Event) Stringers(key string, vals []fmt.Stringer) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = enc.AppendStringers(enc.AppendKey(e.buf, key), vals)
+ return e
+}
+
+// Bytes adds the field key with val as a string to the *Event context.
+//
+// Runes outside of normal ASCII ranges will be hex-encoded in the resulting
+// JSON.
+func (e *Event) Bytes(key string, val []byte) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = enc.AppendBytes(enc.AppendKey(e.buf, key), val)
+ return e
+}
+
+// Hex adds the field key with val as a hex string to the *Event context.
+func (e *Event) Hex(key string, val []byte) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = enc.AppendHex(enc.AppendKey(e.buf, key), val)
+ return e
+}
+
+// RawJSON adds already encoded JSON to the log line under key.
+//
+// No sanity check is performed on b; it must not contain carriage returns and
+// be valid JSON.
+func (e *Event) RawJSON(key string, b []byte) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = appendJSON(enc.AppendKey(e.buf, key), b)
+ return e
+}
+
+// RawCBOR adds already encoded CBOR to the log line under key.
+//
+// No sanity check is performed on b
+// Note: The full featureset of CBOR is supported as data will not be mapped to json but stored as data-url
+func (e *Event) RawCBOR(key string, b []byte) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = appendCBOR(enc.AppendKey(e.buf, key), b)
+ return e
+}
+
+// AnErr adds the field key with serialized err to the *Event context.
+// If err is nil, no field is added.
+func (e *Event) AnErr(key string, err error) *Event {
+ if e == nil {
+ return e
+ }
+ switch m := ErrorMarshalFunc(err).(type) {
+ case nil:
+ return e
+ case LogObjectMarshaler:
+ return e.Object(key, m)
+ case error:
+ if m == nil || isNilValue(m) {
+ return e
+ } else {
+ return e.Str(key, m.Error())
+ }
+ case string:
+ return e.Str(key, m)
+ default:
+ return e.Interface(key, m)
+ }
+}
+
+// Errs adds the field key with errs as an array of serialized errors to the
+// *Event context.
+func (e *Event) Errs(key string, errs []error) *Event {
+ if e == nil {
+ return e
+ }
+ arr := Arr()
+ for _, err := range errs {
+ switch m := ErrorMarshalFunc(err).(type) {
+ case LogObjectMarshaler:
+ arr = arr.Object(m)
+ case error:
+ arr = arr.Err(m)
+ case string:
+ arr = arr.Str(m)
+ default:
+ arr = arr.Interface(m)
+ }
+ }
+
+ return e.Array(key, arr)
+}
+
+// Err adds the field "error" with serialized err to the *Event context.
+// If err is nil, no field is added.
+//
+// To customize the key name, change zerolog.ErrorFieldName.
+//
+// If Stack() has been called before and zerolog.ErrorStackMarshaler is defined,
+// the err is passed to ErrorStackMarshaler and the result is appended to the
+// zerolog.ErrorStackFieldName.
+func (e *Event) Err(err error) *Event {
+ if e == nil {
+ return e
+ }
+ if e.stack && ErrorStackMarshaler != nil {
+ switch m := ErrorStackMarshaler(err).(type) {
+ case nil:
+ case LogObjectMarshaler:
+ e.Object(ErrorStackFieldName, m)
+ case error:
+ if m != nil && !isNilValue(m) {
+ e.Str(ErrorStackFieldName, m.Error())
+ }
+ case string:
+ e.Str(ErrorStackFieldName, m)
+ default:
+ e.Interface(ErrorStackFieldName, m)
+ }
+ }
+ return e.AnErr(ErrorFieldName, err)
+}
+
+// Stack enables stack trace printing for the error passed to Err().
+//
+// ErrorStackMarshaler must be set for this method to do something.
+func (e *Event) Stack() *Event {
+ if e != nil {
+ e.stack = true
+ }
+ return e
+}
+
+// Ctx adds the Go Context to the *Event context. The context is not rendered
+// in the output message, but is available to hooks and to Func() calls via the
+// GetCtx() accessor. A typical use case is to extract tracing information from
+// the Go Ctx.
+func (e *Event) Ctx(ctx context.Context) *Event {
+ if e != nil {
+ e.ctx = ctx
+ }
+ return e
+}
+
+// GetCtx retrieves the Go context.Context which is optionally stored in the
+// Event. This allows Hooks and functions passed to Func() to retrieve values
+// which are stored in the context.Context. This can be useful in tracing,
+// where span information is commonly propagated in the context.Context.
+func (e *Event) GetCtx() context.Context {
+ if e == nil || e.ctx == nil {
+ return context.Background()
+ }
+ return e.ctx
+}
+
+// Bool adds the field key with val as a bool to the *Event context.
+func (e *Event) Bool(key string, b bool) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = enc.AppendBool(enc.AppendKey(e.buf, key), b)
+ return e
+}
+
+// Bools adds the field key with val as a []bool to the *Event context.
+func (e *Event) Bools(key string, b []bool) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = enc.AppendBools(enc.AppendKey(e.buf, key), b)
+ return e
+}
+
+// Int adds the field key with i as a int to the *Event context.
+func (e *Event) Int(key string, i int) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = enc.AppendInt(enc.AppendKey(e.buf, key), i)
+ return e
+}
+
+// Ints adds the field key with i as a []int to the *Event context.
+func (e *Event) Ints(key string, i []int) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = enc.AppendInts(enc.AppendKey(e.buf, key), i)
+ return e
+}
+
+// Int8 adds the field key with i as a int8 to the *Event context.
+func (e *Event) Int8(key string, i int8) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = enc.AppendInt8(enc.AppendKey(e.buf, key), i)
+ return e
+}
+
+// Ints8 adds the field key with i as a []int8 to the *Event context.
+func (e *Event) Ints8(key string, i []int8) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = enc.AppendInts8(enc.AppendKey(e.buf, key), i)
+ return e
+}
+
+// Int16 adds the field key with i as a int16 to the *Event context.
+func (e *Event) Int16(key string, i int16) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = enc.AppendInt16(enc.AppendKey(e.buf, key), i)
+ return e
+}
+
+// Ints16 adds the field key with i as a []int16 to the *Event context.
+func (e *Event) Ints16(key string, i []int16) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = enc.AppendInts16(enc.AppendKey(e.buf, key), i)
+ return e
+}
+
+// Int32 adds the field key with i as a int32 to the *Event context.
+func (e *Event) Int32(key string, i int32) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = enc.AppendInt32(enc.AppendKey(e.buf, key), i)
+ return e
+}
+
+// Ints32 adds the field key with i as a []int32 to the *Event context.
+func (e *Event) Ints32(key string, i []int32) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = enc.AppendInts32(enc.AppendKey(e.buf, key), i)
+ return e
+}
+
+// Int64 adds the field key with i as a int64 to the *Event context.
+func (e *Event) Int64(key string, i int64) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = enc.AppendInt64(enc.AppendKey(e.buf, key), i)
+ return e
+}
+
+// Ints64 adds the field key with i as a []int64 to the *Event context.
+func (e *Event) Ints64(key string, i []int64) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = enc.AppendInts64(enc.AppendKey(e.buf, key), i)
+ return e
+}
+
+// Uint adds the field key with i as a uint to the *Event context.
+func (e *Event) Uint(key string, i uint) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = enc.AppendUint(enc.AppendKey(e.buf, key), i)
+ return e
+}
+
+// Uints adds the field key with i as a []int to the *Event context.
+func (e *Event) Uints(key string, i []uint) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = enc.AppendUints(enc.AppendKey(e.buf, key), i)
+ return e
+}
+
+// Uint8 adds the field key with i as a uint8 to the *Event context.
+func (e *Event) Uint8(key string, i uint8) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = enc.AppendUint8(enc.AppendKey(e.buf, key), i)
+ return e
+}
+
+// Uints8 adds the field key with i as a []int8 to the *Event context.
+func (e *Event) Uints8(key string, i []uint8) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = enc.AppendUints8(enc.AppendKey(e.buf, key), i)
+ return e
+}
+
+// Uint16 adds the field key with i as a uint16 to the *Event context.
+func (e *Event) Uint16(key string, i uint16) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = enc.AppendUint16(enc.AppendKey(e.buf, key), i)
+ return e
+}
+
+// Uints16 adds the field key with i as a []int16 to the *Event context.
+func (e *Event) Uints16(key string, i []uint16) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = enc.AppendUints16(enc.AppendKey(e.buf, key), i)
+ return e
+}
+
+// Uint32 adds the field key with i as a uint32 to the *Event context.
+func (e *Event) Uint32(key string, i uint32) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = enc.AppendUint32(enc.AppendKey(e.buf, key), i)
+ return e
+}
+
+// Uints32 adds the field key with i as a []int32 to the *Event context.
+func (e *Event) Uints32(key string, i []uint32) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = enc.AppendUints32(enc.AppendKey(e.buf, key), i)
+ return e
+}
+
+// Uint64 adds the field key with i as a uint64 to the *Event context.
+func (e *Event) Uint64(key string, i uint64) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = enc.AppendUint64(enc.AppendKey(e.buf, key), i)
+ return e
+}
+
+// Uints64 adds the field key with i as a []int64 to the *Event context.
+func (e *Event) Uints64(key string, i []uint64) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = enc.AppendUints64(enc.AppendKey(e.buf, key), i)
+ return e
+}
+
+// Float32 adds the field key with f as a float32 to the *Event context.
+func (e *Event) Float32(key string, f float32) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = enc.AppendFloat32(enc.AppendKey(e.buf, key), f, FloatingPointPrecision)
+ return e
+}
+
+// Floats32 adds the field key with f as a []float32 to the *Event context.
+func (e *Event) Floats32(key string, f []float32) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = enc.AppendFloats32(enc.AppendKey(e.buf, key), f, FloatingPointPrecision)
+ return e
+}
+
+// Float64 adds the field key with f as a float64 to the *Event context.
+func (e *Event) Float64(key string, f float64) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = enc.AppendFloat64(enc.AppendKey(e.buf, key), f, FloatingPointPrecision)
+ return e
+}
+
+// Floats64 adds the field key with f as a []float64 to the *Event context.
+func (e *Event) Floats64(key string, f []float64) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = enc.AppendFloats64(enc.AppendKey(e.buf, key), f, FloatingPointPrecision)
+ return e
+}
+
+// Timestamp adds the current local time as UNIX timestamp to the *Event context with the "time" key.
+// To customize the key name, change zerolog.TimestampFieldName.
+//
+// NOTE: It won't dedupe the "time" key if the *Event (or *Context) has one
+// already.
+func (e *Event) Timestamp() *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = enc.AppendTime(enc.AppendKey(e.buf, TimestampFieldName), TimestampFunc(), TimeFieldFormat)
+ return e
+}
+
+// Time adds the field key with t formatted as string using zerolog.TimeFieldFormat.
+func (e *Event) Time(key string, t time.Time) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = enc.AppendTime(enc.AppendKey(e.buf, key), t, TimeFieldFormat)
+ return e
+}
+
+// Times adds the field key with t formatted as string using zerolog.TimeFieldFormat.
+func (e *Event) Times(key string, t []time.Time) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = enc.AppendTimes(enc.AppendKey(e.buf, key), t, TimeFieldFormat)
+ return e
+}
+
+// Dur adds the field key with duration d stored as zerolog.DurationFieldUnit.
+// If zerolog.DurationFieldInteger is true, durations are rendered as integer
+// instead of float.
+func (e *Event) Dur(key string, d time.Duration) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = enc.AppendDuration(enc.AppendKey(e.buf, key), d, DurationFieldUnit, DurationFieldInteger, FloatingPointPrecision)
+ return e
+}
+
+// Durs adds the field key with duration d stored as zerolog.DurationFieldUnit.
+// If zerolog.DurationFieldInteger is true, durations are rendered as integer
+// instead of float.
+func (e *Event) Durs(key string, d []time.Duration) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = enc.AppendDurations(enc.AppendKey(e.buf, key), d, DurationFieldUnit, DurationFieldInteger, FloatingPointPrecision)
+ return e
+}
+
+// TimeDiff adds the field key with positive duration between time t and start.
+// If time t is not greater than start, duration will be 0.
+// Duration format follows the same principle as Dur().
+func (e *Event) TimeDiff(key string, t time.Time, start time.Time) *Event {
+ if e == nil {
+ return e
+ }
+ var d time.Duration
+ if t.After(start) {
+ d = t.Sub(start)
+ }
+ e.buf = enc.AppendDuration(enc.AppendKey(e.buf, key), d, DurationFieldUnit, DurationFieldInteger, FloatingPointPrecision)
+ return e
+}
+
+// Any is a wrapper around Event.Interface.
+func (e *Event) Any(key string, i interface{}) *Event {
+ return e.Interface(key, i)
+}
+
+// Interface adds the field key with i marshaled using reflection.
+func (e *Event) Interface(key string, i interface{}) *Event {
+ if e == nil {
+ return e
+ }
+ if obj, ok := i.(LogObjectMarshaler); ok {
+ return e.Object(key, obj)
+ }
+ e.buf = enc.AppendInterface(enc.AppendKey(e.buf, key), i)
+ return e
+}
+
+// Type adds the field key with val's type using reflection.
+func (e *Event) Type(key string, val interface{}) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = enc.AppendType(enc.AppendKey(e.buf, key), val)
+ return e
+}
+
+// CallerSkipFrame instructs any future Caller calls to skip the specified number of frames.
+// This includes those added via hooks from the context.
+func (e *Event) CallerSkipFrame(skip int) *Event {
+ if e == nil {
+ return e
+ }
+ e.skipFrame += skip
+ return e
+}
+
+// Caller adds the file:line of the caller with the zerolog.CallerFieldName key.
+// The argument skip is the number of stack frames to ascend
+// Skip If not passed, use the global variable CallerSkipFrameCount
+func (e *Event) Caller(skip ...int) *Event {
+ sk := CallerSkipFrameCount
+ if len(skip) > 0 {
+ sk = skip[0] + CallerSkipFrameCount
+ }
+ return e.caller(sk)
+}
+
+func (e *Event) caller(skip int) *Event {
+ if e == nil {
+ return e
+ }
+ pc, file, line, ok := runtime.Caller(skip + e.skipFrame)
+ if !ok {
+ return e
+ }
+ e.buf = enc.AppendString(enc.AppendKey(e.buf, CallerFieldName), CallerMarshalFunc(pc, file, line))
+ return e
+}
+
+// IPAddr adds IPv4 or IPv6 Address to the event
+func (e *Event) IPAddr(key string, ip net.IP) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = enc.AppendIPAddr(enc.AppendKey(e.buf, key), ip)
+ return e
+}
+
+// IPPrefix adds IPv4 or IPv6 Prefix (address and mask) to the event
+func (e *Event) IPPrefix(key string, pfx net.IPNet) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = enc.AppendIPPrefix(enc.AppendKey(e.buf, key), pfx)
+ return e
+}
+
+// MACAddr adds MAC address to the event
+func (e *Event) MACAddr(key string, ha net.HardwareAddr) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = enc.AppendMACAddr(enc.AppendKey(e.buf, key), ha)
+ return e
+}
diff --git a/vendor/github.com/rs/zerolog/example.jsonl b/vendor/github.com/rs/zerolog/example.jsonl
new file mode 100644
index 00000000..d73193d7
--- /dev/null
+++ b/vendor/github.com/rs/zerolog/example.jsonl
@@ -0,0 +1,7 @@
+{"time":"5:41PM","level":"info","message":"Starting listener","listen":":8080","pid":37556}
+{"time":"5:41PM","level":"debug","message":"Access","database":"myapp","host":"localhost:4962","pid":37556}
+{"time":"5:41PM","level":"info","message":"Access","method":"GET","path":"/users","pid":37556,"resp_time":23}
+{"time":"5:41PM","level":"info","message":"Access","method":"POST","path":"/posts","pid":37556,"resp_time":532}
+{"time":"5:41PM","level":"warn","message":"Slow request","method":"POST","path":"/posts","pid":37556,"resp_time":532}
+{"time":"5:41PM","level":"info","message":"Access","method":"GET","path":"/users","pid":37556,"resp_time":10}
+{"time":"5:41PM","level":"error","message":"Database connection lost","database":"myapp","pid":37556,"error":"connection reset by peer"}
diff --git a/vendor/github.com/rs/zerolog/fields.go b/vendor/github.com/rs/zerolog/fields.go
new file mode 100644
index 00000000..99f52718
--- /dev/null
+++ b/vendor/github.com/rs/zerolog/fields.go
@@ -0,0 +1,292 @@
+package zerolog
+
+import (
+ "encoding/json"
+ "net"
+ "sort"
+ "time"
+ "unsafe"
+)
+
+func isNilValue(i interface{}) bool {
+ return (*[2]uintptr)(unsafe.Pointer(&i))[1] == 0
+}
+
+func appendFields(dst []byte, fields interface{}, stack bool) []byte {
+ switch fields := fields.(type) {
+ case []interface{}:
+ if n := len(fields); n&0x1 == 1 { // odd number
+ fields = fields[:n-1]
+ }
+ dst = appendFieldList(dst, fields, stack)
+ case map[string]interface{}:
+ keys := make([]string, 0, len(fields))
+ for key := range fields {
+ keys = append(keys, key)
+ }
+ sort.Strings(keys)
+ kv := make([]interface{}, 2)
+ for _, key := range keys {
+ kv[0], kv[1] = key, fields[key]
+ dst = appendFieldList(dst, kv, stack)
+ }
+ }
+ return dst
+}
+
+func appendFieldList(dst []byte, kvList []interface{}, stack bool) []byte {
+ for i, n := 0, len(kvList); i < n; i += 2 {
+ key, val := kvList[i], kvList[i+1]
+ if key, ok := key.(string); ok {
+ dst = enc.AppendKey(dst, key)
+ } else {
+ continue
+ }
+ if val, ok := val.(LogObjectMarshaler); ok {
+ e := newEvent(nil, 0)
+ e.buf = e.buf[:0]
+ e.appendObject(val)
+ dst = append(dst, e.buf...)
+ putEvent(e)
+ continue
+ }
+ switch val := val.(type) {
+ case string:
+ dst = enc.AppendString(dst, val)
+ case []byte:
+ dst = enc.AppendBytes(dst, val)
+ case error:
+ switch m := ErrorMarshalFunc(val).(type) {
+ case LogObjectMarshaler:
+ e := newEvent(nil, 0)
+ e.buf = e.buf[:0]
+ e.appendObject(m)
+ dst = append(dst, e.buf...)
+ putEvent(e)
+ case error:
+ if m == nil || isNilValue(m) {
+ dst = enc.AppendNil(dst)
+ } else {
+ dst = enc.AppendString(dst, m.Error())
+ }
+ case string:
+ dst = enc.AppendString(dst, m)
+ default:
+ dst = enc.AppendInterface(dst, m)
+ }
+
+ if stack && ErrorStackMarshaler != nil {
+ dst = enc.AppendKey(dst, ErrorStackFieldName)
+ switch m := ErrorStackMarshaler(val).(type) {
+ case nil:
+ case error:
+ if m != nil && !isNilValue(m) {
+ dst = enc.AppendString(dst, m.Error())
+ }
+ case string:
+ dst = enc.AppendString(dst, m)
+ default:
+ dst = enc.AppendInterface(dst, m)
+ }
+ }
+ case []error:
+ dst = enc.AppendArrayStart(dst)
+ for i, err := range val {
+ switch m := ErrorMarshalFunc(err).(type) {
+ case LogObjectMarshaler:
+ e := newEvent(nil, 0)
+ e.buf = e.buf[:0]
+ e.appendObject(m)
+ dst = append(dst, e.buf...)
+ putEvent(e)
+ case error:
+ if m == nil || isNilValue(m) {
+ dst = enc.AppendNil(dst)
+ } else {
+ dst = enc.AppendString(dst, m.Error())
+ }
+ case string:
+ dst = enc.AppendString(dst, m)
+ default:
+ dst = enc.AppendInterface(dst, m)
+ }
+
+ if i < (len(val) - 1) {
+ enc.AppendArrayDelim(dst)
+ }
+ }
+ dst = enc.AppendArrayEnd(dst)
+ case bool:
+ dst = enc.AppendBool(dst, val)
+ case int:
+ dst = enc.AppendInt(dst, val)
+ case int8:
+ dst = enc.AppendInt8(dst, val)
+ case int16:
+ dst = enc.AppendInt16(dst, val)
+ case int32:
+ dst = enc.AppendInt32(dst, val)
+ case int64:
+ dst = enc.AppendInt64(dst, val)
+ case uint:
+ dst = enc.AppendUint(dst, val)
+ case uint8:
+ dst = enc.AppendUint8(dst, val)
+ case uint16:
+ dst = enc.AppendUint16(dst, val)
+ case uint32:
+ dst = enc.AppendUint32(dst, val)
+ case uint64:
+ dst = enc.AppendUint64(dst, val)
+ case float32:
+ dst = enc.AppendFloat32(dst, val, FloatingPointPrecision)
+ case float64:
+ dst = enc.AppendFloat64(dst, val, FloatingPointPrecision)
+ case time.Time:
+ dst = enc.AppendTime(dst, val, TimeFieldFormat)
+ case time.Duration:
+ dst = enc.AppendDuration(dst, val, DurationFieldUnit, DurationFieldInteger, FloatingPointPrecision)
+ case *string:
+ if val != nil {
+ dst = enc.AppendString(dst, *val)
+ } else {
+ dst = enc.AppendNil(dst)
+ }
+ case *bool:
+ if val != nil {
+ dst = enc.AppendBool(dst, *val)
+ } else {
+ dst = enc.AppendNil(dst)
+ }
+ case *int:
+ if val != nil {
+ dst = enc.AppendInt(dst, *val)
+ } else {
+ dst = enc.AppendNil(dst)
+ }
+ case *int8:
+ if val != nil {
+ dst = enc.AppendInt8(dst, *val)
+ } else {
+ dst = enc.AppendNil(dst)
+ }
+ case *int16:
+ if val != nil {
+ dst = enc.AppendInt16(dst, *val)
+ } else {
+ dst = enc.AppendNil(dst)
+ }
+ case *int32:
+ if val != nil {
+ dst = enc.AppendInt32(dst, *val)
+ } else {
+ dst = enc.AppendNil(dst)
+ }
+ case *int64:
+ if val != nil {
+ dst = enc.AppendInt64(dst, *val)
+ } else {
+ dst = enc.AppendNil(dst)
+ }
+ case *uint:
+ if val != nil {
+ dst = enc.AppendUint(dst, *val)
+ } else {
+ dst = enc.AppendNil(dst)
+ }
+ case *uint8:
+ if val != nil {
+ dst = enc.AppendUint8(dst, *val)
+ } else {
+ dst = enc.AppendNil(dst)
+ }
+ case *uint16:
+ if val != nil {
+ dst = enc.AppendUint16(dst, *val)
+ } else {
+ dst = enc.AppendNil(dst)
+ }
+ case *uint32:
+ if val != nil {
+ dst = enc.AppendUint32(dst, *val)
+ } else {
+ dst = enc.AppendNil(dst)
+ }
+ case *uint64:
+ if val != nil {
+ dst = enc.AppendUint64(dst, *val)
+ } else {
+ dst = enc.AppendNil(dst)
+ }
+ case *float32:
+ if val != nil {
+ dst = enc.AppendFloat32(dst, *val, FloatingPointPrecision)
+ } else {
+ dst = enc.AppendNil(dst)
+ }
+ case *float64:
+ if val != nil {
+ dst = enc.AppendFloat64(dst, *val, FloatingPointPrecision)
+ } else {
+ dst = enc.AppendNil(dst)
+ }
+ case *time.Time:
+ if val != nil {
+ dst = enc.AppendTime(dst, *val, TimeFieldFormat)
+ } else {
+ dst = enc.AppendNil(dst)
+ }
+ case *time.Duration:
+ if val != nil {
+ dst = enc.AppendDuration(dst, *val, DurationFieldUnit, DurationFieldInteger, FloatingPointPrecision)
+ } else {
+ dst = enc.AppendNil(dst)
+ }
+ case []string:
+ dst = enc.AppendStrings(dst, val)
+ case []bool:
+ dst = enc.AppendBools(dst, val)
+ case []int:
+ dst = enc.AppendInts(dst, val)
+ case []int8:
+ dst = enc.AppendInts8(dst, val)
+ case []int16:
+ dst = enc.AppendInts16(dst, val)
+ case []int32:
+ dst = enc.AppendInts32(dst, val)
+ case []int64:
+ dst = enc.AppendInts64(dst, val)
+ case []uint:
+ dst = enc.AppendUints(dst, val)
+ // case []uint8:
+ // dst = enc.AppendUints8(dst, val)
+ case []uint16:
+ dst = enc.AppendUints16(dst, val)
+ case []uint32:
+ dst = enc.AppendUints32(dst, val)
+ case []uint64:
+ dst = enc.AppendUints64(dst, val)
+ case []float32:
+ dst = enc.AppendFloats32(dst, val, FloatingPointPrecision)
+ case []float64:
+ dst = enc.AppendFloats64(dst, val, FloatingPointPrecision)
+ case []time.Time:
+ dst = enc.AppendTimes(dst, val, TimeFieldFormat)
+ case []time.Duration:
+ dst = enc.AppendDurations(dst, val, DurationFieldUnit, DurationFieldInteger, FloatingPointPrecision)
+ case nil:
+ dst = enc.AppendNil(dst)
+ case net.IP:
+ dst = enc.AppendIPAddr(dst, val)
+ case net.IPNet:
+ dst = enc.AppendIPPrefix(dst, val)
+ case net.HardwareAddr:
+ dst = enc.AppendMACAddr(dst, val)
+ case json.RawMessage:
+ dst = appendJSON(dst, val)
+ default:
+ dst = enc.AppendInterface(dst, val)
+ }
+ }
+ return dst
+}
diff --git a/vendor/github.com/rs/zerolog/globals.go b/vendor/github.com/rs/zerolog/globals.go
new file mode 100644
index 00000000..9a9be713
--- /dev/null
+++ b/vendor/github.com/rs/zerolog/globals.go
@@ -0,0 +1,190 @@
+package zerolog
+
+import (
+ "bytes"
+ "encoding/json"
+ "strconv"
+ "sync/atomic"
+ "time"
+)
+
+const (
+ // TimeFormatUnix defines a time format that makes time fields to be
+ // serialized as Unix timestamp integers.
+ TimeFormatUnix = ""
+
+ // TimeFormatUnixMs defines a time format that makes time fields to be
+ // serialized as Unix timestamp integers in milliseconds.
+ TimeFormatUnixMs = "UNIXMS"
+
+ // TimeFormatUnixMicro defines a time format that makes time fields to be
+ // serialized as Unix timestamp integers in microseconds.
+ TimeFormatUnixMicro = "UNIXMICRO"
+
+ // TimeFormatUnixNano defines a time format that makes time fields to be
+ // serialized as Unix timestamp integers in nanoseconds.
+ TimeFormatUnixNano = "UNIXNANO"
+)
+
+var (
+ // TimestampFieldName is the field name used for the timestamp field.
+ TimestampFieldName = "time"
+
+ // LevelFieldName is the field name used for the level field.
+ LevelFieldName = "level"
+
+ // LevelTraceValue is the value used for the trace level field.
+ LevelTraceValue = "trace"
+ // LevelDebugValue is the value used for the debug level field.
+ LevelDebugValue = "debug"
+ // LevelInfoValue is the value used for the info level field.
+ LevelInfoValue = "info"
+ // LevelWarnValue is the value used for the warn level field.
+ LevelWarnValue = "warn"
+ // LevelErrorValue is the value used for the error level field.
+ LevelErrorValue = "error"
+ // LevelFatalValue is the value used for the fatal level field.
+ LevelFatalValue = "fatal"
+ // LevelPanicValue is the value used for the panic level field.
+ LevelPanicValue = "panic"
+
+ // LevelFieldMarshalFunc allows customization of global level field marshaling.
+ LevelFieldMarshalFunc = func(l Level) string {
+ return l.String()
+ }
+
+ // MessageFieldName is the field name used for the message field.
+ MessageFieldName = "message"
+
+ // ErrorFieldName is the field name used for error fields.
+ ErrorFieldName = "error"
+
+ // CallerFieldName is the field name used for caller field.
+ CallerFieldName = "caller"
+
+ // CallerSkipFrameCount is the number of stack frames to skip to find the caller.
+ CallerSkipFrameCount = 2
+
+ // CallerMarshalFunc allows customization of global caller marshaling
+ CallerMarshalFunc = func(pc uintptr, file string, line int) string {
+ return file + ":" + strconv.Itoa(line)
+ }
+
+ // ErrorStackFieldName is the field name used for error stacks.
+ ErrorStackFieldName = "stack"
+
+ // ErrorStackMarshaler extract the stack from err if any.
+ ErrorStackMarshaler func(err error) interface{}
+
+ // ErrorMarshalFunc allows customization of global error marshaling
+ ErrorMarshalFunc = func(err error) interface{} {
+ return err
+ }
+
+ // InterfaceMarshalFunc allows customization of interface marshaling.
+ // Default: "encoding/json.Marshal" with disabled HTML escaping
+ InterfaceMarshalFunc = func(v interface{}) ([]byte, error) {
+ var buf bytes.Buffer
+ encoder := json.NewEncoder(&buf)
+ encoder.SetEscapeHTML(false)
+ err := encoder.Encode(v)
+ if err != nil {
+ return nil, err
+ }
+ b := buf.Bytes()
+ if len(b) > 0 {
+ // Remove trailing \n which is added by Encode.
+ return b[:len(b)-1], nil
+ }
+ return b, nil
+ }
+
+ // TimeFieldFormat defines the time format of the Time field type. If set to
+ // TimeFormatUnix, TimeFormatUnixMs, TimeFormatUnixMicro or TimeFormatUnixNano, the time is formatted as a UNIX
+ // timestamp as integer.
+ TimeFieldFormat = time.RFC3339
+
+ // TimestampFunc defines the function called to generate a timestamp.
+ TimestampFunc = time.Now
+
+ // DurationFieldUnit defines the unit for time.Duration type fields added
+ // using the Dur method.
+ DurationFieldUnit = time.Millisecond
+
+ // DurationFieldInteger renders Dur fields as integer instead of float if
+ // set to true.
+ DurationFieldInteger = false
+
+ // ErrorHandler is called whenever zerolog fails to write an event on its
+ // output. If not set, an error is printed on the stderr. This handler must
+ // be thread safe and non-blocking.
+ ErrorHandler func(err error)
+
+ // DefaultContextLogger is returned from Ctx() if there is no logger associated
+ // with the context.
+ DefaultContextLogger *Logger
+
+ // LevelColors are used by ConsoleWriter's consoleDefaultFormatLevel to color
+ // log levels.
+ LevelColors = map[Level]int{
+ TraceLevel: colorBlue,
+ DebugLevel: 0,
+ InfoLevel: colorGreen,
+ WarnLevel: colorYellow,
+ ErrorLevel: colorRed,
+ FatalLevel: colorRed,
+ PanicLevel: colorRed,
+ }
+
+ // FormattedLevels are used by ConsoleWriter's consoleDefaultFormatLevel
+ // for a short level name.
+ FormattedLevels = map[Level]string{
+ TraceLevel: "TRC",
+ DebugLevel: "DBG",
+ InfoLevel: "INF",
+ WarnLevel: "WRN",
+ ErrorLevel: "ERR",
+ FatalLevel: "FTL",
+ PanicLevel: "PNC",
+ }
+
+ // TriggerLevelWriterBufferReuseLimit is a limit in bytes that a buffer is dropped
+ // from the TriggerLevelWriter buffer pool if the buffer grows above the limit.
+ TriggerLevelWriterBufferReuseLimit = 64 * 1024
+
+ // FloatingPointPrecision, if set to a value other than -1, controls the number
+ // of digits when formatting float numbers in JSON. See strconv.FormatFloat for
+ // more details.
+ FloatingPointPrecision = -1
+)
+
+var (
+ gLevel = new(int32)
+ disableSampling = new(int32)
+)
+
+// SetGlobalLevel sets the global override for log level. If this
+// values is raised, all Loggers will use at least this value.
+//
+// To globally disable logs, set GlobalLevel to Disabled.
+func SetGlobalLevel(l Level) {
+ atomic.StoreInt32(gLevel, int32(l))
+}
+
+// GlobalLevel returns the current global log level
+func GlobalLevel() Level {
+ return Level(atomic.LoadInt32(gLevel))
+}
+
+// DisableSampling will disable sampling in all Loggers if true.
+func DisableSampling(v bool) {
+ var i int32
+ if v {
+ i = 1
+ }
+ atomic.StoreInt32(disableSampling, i)
+}
+
+func samplingDisabled() bool {
+ return atomic.LoadInt32(disableSampling) == 1
+}
diff --git a/vendor/github.com/rs/zerolog/go112.go b/vendor/github.com/rs/zerolog/go112.go
new file mode 100644
index 00000000..e7b5a1bd
--- /dev/null
+++ b/vendor/github.com/rs/zerolog/go112.go
@@ -0,0 +1,7 @@
+// +build go1.12
+
+package zerolog
+
+// Since go 1.12, some auto generated init functions are hidden from
+// runtime.Caller.
+const contextCallerSkipFrameCount = 2
diff --git a/vendor/github.com/rs/zerolog/hook.go b/vendor/github.com/rs/zerolog/hook.go
new file mode 100644
index 00000000..ec6effc1
--- /dev/null
+++ b/vendor/github.com/rs/zerolog/hook.go
@@ -0,0 +1,64 @@
+package zerolog
+
+// Hook defines an interface to a log hook.
+type Hook interface {
+ // Run runs the hook with the event.
+ Run(e *Event, level Level, message string)
+}
+
+// HookFunc is an adaptor to allow the use of an ordinary function
+// as a Hook.
+type HookFunc func(e *Event, level Level, message string)
+
+// Run implements the Hook interface.
+func (h HookFunc) Run(e *Event, level Level, message string) {
+ h(e, level, message)
+}
+
+// LevelHook applies a different hook for each level.
+type LevelHook struct {
+ NoLevelHook, TraceHook, DebugHook, InfoHook, WarnHook, ErrorHook, FatalHook, PanicHook Hook
+}
+
+// Run implements the Hook interface.
+func (h LevelHook) Run(e *Event, level Level, message string) {
+ switch level {
+ case TraceLevel:
+ if h.TraceHook != nil {
+ h.TraceHook.Run(e, level, message)
+ }
+ case DebugLevel:
+ if h.DebugHook != nil {
+ h.DebugHook.Run(e, level, message)
+ }
+ case InfoLevel:
+ if h.InfoHook != nil {
+ h.InfoHook.Run(e, level, message)
+ }
+ case WarnLevel:
+ if h.WarnHook != nil {
+ h.WarnHook.Run(e, level, message)
+ }
+ case ErrorLevel:
+ if h.ErrorHook != nil {
+ h.ErrorHook.Run(e, level, message)
+ }
+ case FatalLevel:
+ if h.FatalHook != nil {
+ h.FatalHook.Run(e, level, message)
+ }
+ case PanicLevel:
+ if h.PanicHook != nil {
+ h.PanicHook.Run(e, level, message)
+ }
+ case NoLevel:
+ if h.NoLevelHook != nil {
+ h.NoLevelHook.Run(e, level, message)
+ }
+ }
+}
+
+// NewLevelHook returns a new LevelHook.
+func NewLevelHook() LevelHook {
+ return LevelHook{}
+}
diff --git a/vendor/github.com/rs/zerolog/internal/cbor/README.md b/vendor/github.com/rs/zerolog/internal/cbor/README.md
new file mode 100644
index 00000000..92c2e8c7
--- /dev/null
+++ b/vendor/github.com/rs/zerolog/internal/cbor/README.md
@@ -0,0 +1,56 @@
+## Reference:
+ CBOR Encoding is described in [RFC7049](https://tools.ietf.org/html/rfc7049)
+
+## Comparison of JSON vs CBOR
+
+Two main areas of reduction are:
+
+1. CPU usage to write a log msg
+2. Size (in bytes) of log messages.
+
+
+CPU Usage savings are below:
+```
+name JSON time/op CBOR time/op delta
+Info-32 15.3ns ± 1% 11.7ns ± 3% -23.78% (p=0.000 n=9+10)
+ContextFields-32 16.2ns ± 2% 12.3ns ± 3% -23.97% (p=0.000 n=9+9)
+ContextAppend-32 6.70ns ± 0% 6.20ns ± 0% -7.44% (p=0.000 n=9+9)
+LogFields-32 66.4ns ± 0% 24.6ns ± 2% -62.89% (p=0.000 n=10+9)
+LogArrayObject-32 911ns ±11% 768ns ± 6% -15.64% (p=0.000 n=10+10)
+LogFieldType/Floats-32 70.3ns ± 2% 29.5ns ± 1% -57.98% (p=0.000 n=10+10)
+LogFieldType/Err-32 14.0ns ± 3% 12.1ns ± 8% -13.20% (p=0.000 n=8+10)
+LogFieldType/Dur-32 17.2ns ± 2% 13.1ns ± 1% -24.27% (p=0.000 n=10+9)
+LogFieldType/Object-32 54.3ns ±11% 52.3ns ± 7% ~ (p=0.239 n=10+10)
+LogFieldType/Ints-32 20.3ns ± 2% 15.1ns ± 2% -25.50% (p=0.000 n=9+10)
+LogFieldType/Interfaces-32 642ns ±11% 621ns ± 9% ~ (p=0.118 n=10+10)
+LogFieldType/Interface(Objects)-32 635ns ±13% 632ns ± 9% ~ (p=0.592 n=10+10)
+LogFieldType/Times-32 294ns ± 0% 27ns ± 1% -90.71% (p=0.000 n=10+9)
+LogFieldType/Durs-32 121ns ± 0% 33ns ± 2% -72.44% (p=0.000 n=9+9)
+LogFieldType/Interface(Object)-32 56.6ns ± 8% 52.3ns ± 8% -7.54% (p=0.007 n=10+10)
+LogFieldType/Errs-32 17.8ns ± 3% 16.1ns ± 2% -9.71% (p=0.000 n=10+9)
+LogFieldType/Time-32 40.5ns ± 1% 12.7ns ± 6% -68.66% (p=0.000 n=8+9)
+LogFieldType/Bool-32 12.0ns ± 5% 10.2ns ± 2% -15.18% (p=0.000 n=10+8)
+LogFieldType/Bools-32 17.2ns ± 2% 12.6ns ± 4% -26.63% (p=0.000 n=10+10)
+LogFieldType/Int-32 12.3ns ± 2% 11.2ns ± 4% -9.27% (p=0.000 n=9+10)
+LogFieldType/Float-32 16.7ns ± 1% 12.6ns ± 2% -24.42% (p=0.000 n=7+9)
+LogFieldType/Str-32 12.7ns ± 7% 11.3ns ± 7% -10.88% (p=0.000 n=10+9)
+LogFieldType/Strs-32 20.3ns ± 3% 18.2ns ± 3% -10.25% (p=0.000 n=9+10)
+LogFieldType/Interface-32 183ns ±12% 175ns ± 9% ~ (p=0.078 n=10+10)
+```
+
+Log message size savings is greatly dependent on the number and type of fields in the log message.
+Assuming this log message (with an Integer, timestamp and string, in addition to level).
+
+`{"level":"error","Fault":41650,"time":"2018-04-01T15:18:19-07:00","message":"Some Message"}`
+
+Two measurements were done for the log file sizes - one without any compression, second
+using [compress/zlib](https://golang.org/pkg/compress/zlib/).
+
+Results for 10,000 log messages:
+
+| Log Format | Plain File Size (in KB) | Compressed File Size (in KB) |
+| :--- | :---: | :---: |
+| JSON | 920 | 28 |
+| CBOR | 550 | 28 |
+
+The example used to calculate the above data is available in [Examples](examples).
diff --git a/vendor/github.com/rs/zerolog/internal/cbor/base.go b/vendor/github.com/rs/zerolog/internal/cbor/base.go
new file mode 100644
index 00000000..51fe86c9
--- /dev/null
+++ b/vendor/github.com/rs/zerolog/internal/cbor/base.go
@@ -0,0 +1,19 @@
+package cbor
+
+// JSONMarshalFunc is used to marshal interface to JSON encoded byte slice.
+// Making it package level instead of embedded in Encoder brings
+// some extra efforts at importing, but avoids value copy when the functions
+// of Encoder being invoked.
+// DO REMEMBER to set this variable at importing, or
+// you might get a nil pointer dereference panic at runtime.
+var JSONMarshalFunc func(v interface{}) ([]byte, error)
+
+type Encoder struct{}
+
+// AppendKey adds a key (string) to the binary encoded log message
+func (e Encoder) AppendKey(dst []byte, key string) []byte {
+ if len(dst) < 1 {
+ dst = e.AppendBeginMarker(dst)
+ }
+ return e.AppendString(dst, key)
+}
diff --git a/vendor/github.com/rs/zerolog/internal/cbor/cbor.go b/vendor/github.com/rs/zerolog/internal/cbor/cbor.go
new file mode 100644
index 00000000..1bf14438
--- /dev/null
+++ b/vendor/github.com/rs/zerolog/internal/cbor/cbor.go
@@ -0,0 +1,102 @@
+// Package cbor provides primitives for storing different data
+// in the CBOR (binary) format. CBOR is defined in RFC7049.
+package cbor
+
+import "time"
+
+const (
+ majorOffset = 5
+ additionalMax = 23
+
+ // Non Values.
+ additionalTypeBoolFalse byte = 20
+ additionalTypeBoolTrue byte = 21
+ additionalTypeNull byte = 22
+
+ // Integer (+ve and -ve) Sub-types.
+ additionalTypeIntUint8 byte = 24
+ additionalTypeIntUint16 byte = 25
+ additionalTypeIntUint32 byte = 26
+ additionalTypeIntUint64 byte = 27
+
+ // Float Sub-types.
+ additionalTypeFloat16 byte = 25
+ additionalTypeFloat32 byte = 26
+ additionalTypeFloat64 byte = 27
+ additionalTypeBreak byte = 31
+
+ // Tag Sub-types.
+ additionalTypeTimestamp byte = 01
+ additionalTypeEmbeddedCBOR byte = 63
+
+ // Extended Tags - from https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml
+ additionalTypeTagNetworkAddr uint16 = 260
+ additionalTypeTagNetworkPrefix uint16 = 261
+ additionalTypeEmbeddedJSON uint16 = 262
+ additionalTypeTagHexString uint16 = 263
+
+ // Unspecified number of elements.
+ additionalTypeInfiniteCount byte = 31
+)
+const (
+ majorTypeUnsignedInt byte = iota << majorOffset // Major type 0
+ majorTypeNegativeInt // Major type 1
+ majorTypeByteString // Major type 2
+ majorTypeUtf8String // Major type 3
+ majorTypeArray // Major type 4
+ majorTypeMap // Major type 5
+ majorTypeTags // Major type 6
+ majorTypeSimpleAndFloat // Major type 7
+)
+
+const (
+ maskOutAdditionalType byte = (7 << majorOffset)
+ maskOutMajorType byte = 31
+)
+
+const (
+ float32Nan = "\xfa\x7f\xc0\x00\x00"
+ float32PosInfinity = "\xfa\x7f\x80\x00\x00"
+ float32NegInfinity = "\xfa\xff\x80\x00\x00"
+ float64Nan = "\xfb\x7f\xf8\x00\x00\x00\x00\x00\x00"
+ float64PosInfinity = "\xfb\x7f\xf0\x00\x00\x00\x00\x00\x00"
+ float64NegInfinity = "\xfb\xff\xf0\x00\x00\x00\x00\x00\x00"
+)
+
+// IntegerTimeFieldFormat indicates the format of timestamp decoded
+// from an integer (time in seconds).
+var IntegerTimeFieldFormat = time.RFC3339
+
+// NanoTimeFieldFormat indicates the format of timestamp decoded
+// from a float value (time in seconds and nanoseconds).
+var NanoTimeFieldFormat = time.RFC3339Nano
+
+func appendCborTypePrefix(dst []byte, major byte, number uint64) []byte {
+ byteCount := 8
+ var minor byte
+ switch {
+ case number < 256:
+ byteCount = 1
+ minor = additionalTypeIntUint8
+
+ case number < 65536:
+ byteCount = 2
+ minor = additionalTypeIntUint16
+
+ case number < 4294967296:
+ byteCount = 4
+ minor = additionalTypeIntUint32
+
+ default:
+ byteCount = 8
+ minor = additionalTypeIntUint64
+
+ }
+
+ dst = append(dst, major|minor)
+ byteCount--
+ for ; byteCount >= 0; byteCount-- {
+ dst = append(dst, byte(number>>(uint(byteCount)*8)))
+ }
+ return dst
+}
diff --git a/vendor/github.com/rs/zerolog/internal/cbor/decode_stream.go b/vendor/github.com/rs/zerolog/internal/cbor/decode_stream.go
new file mode 100644
index 00000000..5633e666
--- /dev/null
+++ b/vendor/github.com/rs/zerolog/internal/cbor/decode_stream.go
@@ -0,0 +1,654 @@
+package cbor
+
+// This file contains code to decode a stream of CBOR Data into JSON.
+
+import (
+ "bufio"
+ "bytes"
+ "encoding/base64"
+ "fmt"
+ "io"
+ "math"
+ "net"
+ "runtime"
+ "strconv"
+ "strings"
+ "time"
+ "unicode/utf8"
+)
+
+var decodeTimeZone *time.Location
+
+const hexTable = "0123456789abcdef"
+
+const isFloat32 = 4
+const isFloat64 = 8
+
+func readNBytes(src *bufio.Reader, n int) []byte {
+ ret := make([]byte, n)
+ for i := 0; i < n; i++ {
+ ch, e := src.ReadByte()
+ if e != nil {
+ panic(fmt.Errorf("Tried to Read %d Bytes.. But hit end of file", n))
+ }
+ ret[i] = ch
+ }
+ return ret
+}
+
+func readByte(src *bufio.Reader) byte {
+ b, e := src.ReadByte()
+ if e != nil {
+ panic(fmt.Errorf("Tried to Read 1 Byte.. But hit end of file"))
+ }
+ return b
+}
+
+func decodeIntAdditionalType(src *bufio.Reader, minor byte) int64 {
+ val := int64(0)
+ if minor <= 23 {
+ val = int64(minor)
+ } else {
+ bytesToRead := 0
+ switch minor {
+ case additionalTypeIntUint8:
+ bytesToRead = 1
+ case additionalTypeIntUint16:
+ bytesToRead = 2
+ case additionalTypeIntUint32:
+ bytesToRead = 4
+ case additionalTypeIntUint64:
+ bytesToRead = 8
+ default:
+ panic(fmt.Errorf("Invalid Additional Type: %d in decodeInteger (expected <28)", minor))
+ }
+ pb := readNBytes(src, bytesToRead)
+ for i := 0; i < bytesToRead; i++ {
+ val = val * 256
+ val += int64(pb[i])
+ }
+ }
+ return val
+}
+
+func decodeInteger(src *bufio.Reader) int64 {
+ pb := readByte(src)
+ major := pb & maskOutAdditionalType
+ minor := pb & maskOutMajorType
+ if major != majorTypeUnsignedInt && major != majorTypeNegativeInt {
+ panic(fmt.Errorf("Major type is: %d in decodeInteger!! (expected 0 or 1)", major))
+ }
+ val := decodeIntAdditionalType(src, minor)
+ if major == 0 {
+ return val
+ }
+ return (-1 - val)
+}
+
+func decodeFloat(src *bufio.Reader) (float64, int) {
+ pb := readByte(src)
+ major := pb & maskOutAdditionalType
+ minor := pb & maskOutMajorType
+ if major != majorTypeSimpleAndFloat {
+ panic(fmt.Errorf("Incorrect Major type is: %d in decodeFloat", major))
+ }
+
+ switch minor {
+ case additionalTypeFloat16:
+ panic(fmt.Errorf("float16 is not supported in decodeFloat"))
+
+ case additionalTypeFloat32:
+ pb := readNBytes(src, 4)
+ switch string(pb) {
+ case float32Nan:
+ return math.NaN(), isFloat32
+ case float32PosInfinity:
+ return math.Inf(0), isFloat32
+ case float32NegInfinity:
+ return math.Inf(-1), isFloat32
+ }
+ n := uint32(0)
+ for i := 0; i < 4; i++ {
+ n = n * 256
+ n += uint32(pb[i])
+ }
+ val := math.Float32frombits(n)
+ return float64(val), isFloat32
+ case additionalTypeFloat64:
+ pb := readNBytes(src, 8)
+ switch string(pb) {
+ case float64Nan:
+ return math.NaN(), isFloat64
+ case float64PosInfinity:
+ return math.Inf(0), isFloat64
+ case float64NegInfinity:
+ return math.Inf(-1), isFloat64
+ }
+ n := uint64(0)
+ for i := 0; i < 8; i++ {
+ n = n * 256
+ n += uint64(pb[i])
+ }
+ val := math.Float64frombits(n)
+ return val, isFloat64
+ }
+ panic(fmt.Errorf("Invalid Additional Type: %d in decodeFloat", minor))
+}
+
+func decodeStringComplex(dst []byte, s string, pos uint) []byte {
+ i := int(pos)
+ start := 0
+
+ for i < len(s) {
+ b := s[i]
+ if b >= utf8.RuneSelf {
+ r, size := utf8.DecodeRuneInString(s[i:])
+ if r == utf8.RuneError && size == 1 {
+ // In case of error, first append previous simple characters to
+ // the byte slice if any and append a replacement character code
+ // in place of the invalid sequence.
+ if start < i {
+ dst = append(dst, s[start:i]...)
+ }
+ dst = append(dst, `\ufffd`...)
+ i += size
+ start = i
+ continue
+ }
+ i += size
+ continue
+ }
+ if b >= 0x20 && b <= 0x7e && b != '\\' && b != '"' {
+ i++
+ continue
+ }
+ // We encountered a character that needs to be encoded.
+ // Let's append the previous simple characters to the byte slice
+ // and switch our operation to read and encode the remainder
+ // characters byte-by-byte.
+ if start < i {
+ dst = append(dst, s[start:i]...)
+ }
+ switch b {
+ case '"', '\\':
+ dst = append(dst, '\\', b)
+ case '\b':
+ dst = append(dst, '\\', 'b')
+ case '\f':
+ dst = append(dst, '\\', 'f')
+ case '\n':
+ dst = append(dst, '\\', 'n')
+ case '\r':
+ dst = append(dst, '\\', 'r')
+ case '\t':
+ dst = append(dst, '\\', 't')
+ default:
+ dst = append(dst, '\\', 'u', '0', '0', hexTable[b>>4], hexTable[b&0xF])
+ }
+ i++
+ start = i
+ }
+ if start < len(s) {
+ dst = append(dst, s[start:]...)
+ }
+ return dst
+}
+
+func decodeString(src *bufio.Reader, noQuotes bool) []byte {
+ pb := readByte(src)
+ major := pb & maskOutAdditionalType
+ minor := pb & maskOutMajorType
+ if major != majorTypeByteString {
+ panic(fmt.Errorf("Major type is: %d in decodeString", major))
+ }
+ result := []byte{}
+ if !noQuotes {
+ result = append(result, '"')
+ }
+ length := decodeIntAdditionalType(src, minor)
+ len := int(length)
+ pbs := readNBytes(src, len)
+ result = append(result, pbs...)
+ if noQuotes {
+ return result
+ }
+ return append(result, '"')
+}
+func decodeStringToDataUrl(src *bufio.Reader, mimeType string) []byte {
+ pb := readByte(src)
+ major := pb & maskOutAdditionalType
+ minor := pb & maskOutMajorType
+ if major != majorTypeByteString {
+ panic(fmt.Errorf("Major type is: %d in decodeString", major))
+ }
+ length := decodeIntAdditionalType(src, minor)
+ l := int(length)
+ enc := base64.StdEncoding
+ lEnc := enc.EncodedLen(l)
+ result := make([]byte, len("\"data:;base64,\"")+len(mimeType)+lEnc)
+ dest := result
+ u := copy(dest, "\"data:")
+ dest = dest[u:]
+ u = copy(dest, mimeType)
+ dest = dest[u:]
+ u = copy(dest, ";base64,")
+ dest = dest[u:]
+ pbs := readNBytes(src, l)
+ enc.Encode(dest, pbs)
+ dest = dest[lEnc:]
+ dest[0] = '"'
+ return result
+}
+
+func decodeUTF8String(src *bufio.Reader) []byte {
+ pb := readByte(src)
+ major := pb & maskOutAdditionalType
+ minor := pb & maskOutMajorType
+ if major != majorTypeUtf8String {
+ panic(fmt.Errorf("Major type is: %d in decodeUTF8String", major))
+ }
+ result := []byte{'"'}
+ length := decodeIntAdditionalType(src, minor)
+ len := int(length)
+ pbs := readNBytes(src, len)
+
+ for i := 0; i < len; i++ {
+ // Check if the character needs encoding. Control characters, slashes,
+ // and the double quote need json encoding. Bytes above the ascii
+ // boundary needs utf8 encoding.
+ if pbs[i] < 0x20 || pbs[i] > 0x7e || pbs[i] == '\\' || pbs[i] == '"' {
+ // We encountered a character that needs to be encoded. Switch
+ // to complex version of the algorithm.
+ dst := []byte{'"'}
+ dst = decodeStringComplex(dst, string(pbs), uint(i))
+ return append(dst, '"')
+ }
+ }
+ // The string has no need for encoding and therefore is directly
+ // appended to the byte slice.
+ result = append(result, pbs...)
+ return append(result, '"')
+}
+
+func array2Json(src *bufio.Reader, dst io.Writer) {
+ dst.Write([]byte{'['})
+ pb := readByte(src)
+ major := pb & maskOutAdditionalType
+ minor := pb & maskOutMajorType
+ if major != majorTypeArray {
+ panic(fmt.Errorf("Major type is: %d in array2Json", major))
+ }
+ len := 0
+ unSpecifiedCount := false
+ if minor == additionalTypeInfiniteCount {
+ unSpecifiedCount = true
+ } else {
+ length := decodeIntAdditionalType(src, minor)
+ len = int(length)
+ }
+ for i := 0; unSpecifiedCount || i < len; i++ {
+ if unSpecifiedCount {
+ pb, e := src.Peek(1)
+ if e != nil {
+ panic(e)
+ }
+ if pb[0] == majorTypeSimpleAndFloat|additionalTypeBreak {
+ readByte(src)
+ break
+ }
+ }
+ cbor2JsonOneObject(src, dst)
+ if unSpecifiedCount {
+ pb, e := src.Peek(1)
+ if e != nil {
+ panic(e)
+ }
+ if pb[0] == majorTypeSimpleAndFloat|additionalTypeBreak {
+ readByte(src)
+ break
+ }
+ dst.Write([]byte{','})
+ } else if i+1 < len {
+ dst.Write([]byte{','})
+ }
+ }
+ dst.Write([]byte{']'})
+}
+
+func map2Json(src *bufio.Reader, dst io.Writer) {
+ pb := readByte(src)
+ major := pb & maskOutAdditionalType
+ minor := pb & maskOutMajorType
+ if major != majorTypeMap {
+ panic(fmt.Errorf("Major type is: %d in map2Json", major))
+ }
+ len := 0
+ unSpecifiedCount := false
+ if minor == additionalTypeInfiniteCount {
+ unSpecifiedCount = true
+ } else {
+ length := decodeIntAdditionalType(src, minor)
+ len = int(length)
+ }
+ dst.Write([]byte{'{'})
+ for i := 0; unSpecifiedCount || i < len; i++ {
+ if unSpecifiedCount {
+ pb, e := src.Peek(1)
+ if e != nil {
+ panic(e)
+ }
+ if pb[0] == majorTypeSimpleAndFloat|additionalTypeBreak {
+ readByte(src)
+ break
+ }
+ }
+ cbor2JsonOneObject(src, dst)
+ if i%2 == 0 {
+ // Even position values are keys.
+ dst.Write([]byte{':'})
+ } else {
+ if unSpecifiedCount {
+ pb, e := src.Peek(1)
+ if e != nil {
+ panic(e)
+ }
+ if pb[0] == majorTypeSimpleAndFloat|additionalTypeBreak {
+ readByte(src)
+ break
+ }
+ dst.Write([]byte{','})
+ } else if i+1 < len {
+ dst.Write([]byte{','})
+ }
+ }
+ }
+ dst.Write([]byte{'}'})
+}
+
+func decodeTagData(src *bufio.Reader) []byte {
+ pb := readByte(src)
+ major := pb & maskOutAdditionalType
+ minor := pb & maskOutMajorType
+ if major != majorTypeTags {
+ panic(fmt.Errorf("Major type is: %d in decodeTagData", major))
+ }
+ switch minor {
+ case additionalTypeTimestamp:
+ return decodeTimeStamp(src)
+ case additionalTypeIntUint8:
+ val := decodeIntAdditionalType(src, minor)
+ switch byte(val) {
+ case additionalTypeEmbeddedCBOR:
+ pb := readByte(src)
+ dataMajor := pb & maskOutAdditionalType
+ if dataMajor != majorTypeByteString {
+ panic(fmt.Errorf("Unsupported embedded Type: %d in decodeEmbeddedCBOR", dataMajor))
+ }
+ src.UnreadByte()
+ return decodeStringToDataUrl(src, "application/cbor")
+ default:
+ panic(fmt.Errorf("Unsupported Additional Tag Type: %d in decodeTagData", val))
+ }
+
+ // Tag value is larger than 256 (so uint16).
+ case additionalTypeIntUint16:
+ val := decodeIntAdditionalType(src, minor)
+
+ switch uint16(val) {
+ case additionalTypeEmbeddedJSON:
+ pb := readByte(src)
+ dataMajor := pb & maskOutAdditionalType
+ if dataMajor != majorTypeByteString {
+ panic(fmt.Errorf("Unsupported embedded Type: %d in decodeEmbeddedJSON", dataMajor))
+ }
+ src.UnreadByte()
+ return decodeString(src, true)
+
+ case additionalTypeTagNetworkAddr:
+ octets := decodeString(src, true)
+ ss := []byte{'"'}
+ switch len(octets) {
+ case 6: // MAC address.
+ ha := net.HardwareAddr(octets)
+ ss = append(append(ss, ha.String()...), '"')
+ case 4: // IPv4 address.
+ fallthrough
+ case 16: // IPv6 address.
+ ip := net.IP(octets)
+ ss = append(append(ss, ip.String()...), '"')
+ default:
+ panic(fmt.Errorf("Unexpected Network Address length: %d (expected 4,6,16)", len(octets)))
+ }
+ return ss
+
+ case additionalTypeTagNetworkPrefix:
+ pb := readByte(src)
+ if pb != majorTypeMap|0x1 {
+ panic(fmt.Errorf("IP Prefix is NOT of MAP of 1 elements as expected"))
+ }
+ octets := decodeString(src, true)
+ val := decodeInteger(src)
+ ip := net.IP(octets)
+ var mask net.IPMask
+ pfxLen := int(val)
+ if len(octets) == 4 {
+ mask = net.CIDRMask(pfxLen, 32)
+ } else {
+ mask = net.CIDRMask(pfxLen, 128)
+ }
+ ipPfx := net.IPNet{IP: ip, Mask: mask}
+ ss := []byte{'"'}
+ ss = append(append(ss, ipPfx.String()...), '"')
+ return ss
+
+ case additionalTypeTagHexString:
+ octets := decodeString(src, true)
+ ss := []byte{'"'}
+ for _, v := range octets {
+ ss = append(ss, hexTable[v>>4], hexTable[v&0x0f])
+ }
+ return append(ss, '"')
+
+ default:
+ panic(fmt.Errorf("Unsupported Additional Tag Type: %d in decodeTagData", val))
+ }
+ }
+ panic(fmt.Errorf("Unsupported Additional Type: %d in decodeTagData", minor))
+}
+
+func decodeTimeStamp(src *bufio.Reader) []byte {
+ pb := readByte(src)
+ src.UnreadByte()
+ tsMajor := pb & maskOutAdditionalType
+ if tsMajor == majorTypeUnsignedInt || tsMajor == majorTypeNegativeInt {
+ n := decodeInteger(src)
+ t := time.Unix(n, 0)
+ if decodeTimeZone != nil {
+ t = t.In(decodeTimeZone)
+ } else {
+ t = t.In(time.UTC)
+ }
+ tsb := []byte{}
+ tsb = append(tsb, '"')
+ tsb = t.AppendFormat(tsb, IntegerTimeFieldFormat)
+ tsb = append(tsb, '"')
+ return tsb
+ } else if tsMajor == majorTypeSimpleAndFloat {
+ n, _ := decodeFloat(src)
+ secs := int64(n)
+ n -= float64(secs)
+ n *= float64(1e9)
+ t := time.Unix(secs, int64(n))
+ if decodeTimeZone != nil {
+ t = t.In(decodeTimeZone)
+ } else {
+ t = t.In(time.UTC)
+ }
+ tsb := []byte{}
+ tsb = append(tsb, '"')
+ tsb = t.AppendFormat(tsb, NanoTimeFieldFormat)
+ tsb = append(tsb, '"')
+ return tsb
+ }
+ panic(fmt.Errorf("TS format is neigther int nor float: %d", tsMajor))
+}
+
+func decodeSimpleFloat(src *bufio.Reader) []byte {
+ pb := readByte(src)
+ major := pb & maskOutAdditionalType
+ minor := pb & maskOutMajorType
+ if major != majorTypeSimpleAndFloat {
+ panic(fmt.Errorf("Major type is: %d in decodeSimpleFloat", major))
+ }
+ switch minor {
+ case additionalTypeBoolTrue:
+ return []byte("true")
+ case additionalTypeBoolFalse:
+ return []byte("false")
+ case additionalTypeNull:
+ return []byte("null")
+ case additionalTypeFloat16:
+ fallthrough
+ case additionalTypeFloat32:
+ fallthrough
+ case additionalTypeFloat64:
+ src.UnreadByte()
+ v, bc := decodeFloat(src)
+ ba := []byte{}
+ switch {
+ case math.IsNaN(v):
+ return []byte("\"NaN\"")
+ case math.IsInf(v, 1):
+ return []byte("\"+Inf\"")
+ case math.IsInf(v, -1):
+ return []byte("\"-Inf\"")
+ }
+ if bc == isFloat32 {
+ ba = strconv.AppendFloat(ba, v, 'f', -1, 32)
+ } else if bc == isFloat64 {
+ ba = strconv.AppendFloat(ba, v, 'f', -1, 64)
+ } else {
+ panic(fmt.Errorf("Invalid Float precision from decodeFloat: %d", bc))
+ }
+ return ba
+ default:
+ panic(fmt.Errorf("Invalid Additional Type: %d in decodeSimpleFloat", minor))
+ }
+}
+
+func cbor2JsonOneObject(src *bufio.Reader, dst io.Writer) {
+ pb, e := src.Peek(1)
+ if e != nil {
+ panic(e)
+ }
+ major := (pb[0] & maskOutAdditionalType)
+
+ switch major {
+ case majorTypeUnsignedInt:
+ fallthrough
+ case majorTypeNegativeInt:
+ n := decodeInteger(src)
+ dst.Write([]byte(strconv.Itoa(int(n))))
+
+ case majorTypeByteString:
+ s := decodeString(src, false)
+ dst.Write(s)
+
+ case majorTypeUtf8String:
+ s := decodeUTF8String(src)
+ dst.Write(s)
+
+ case majorTypeArray:
+ array2Json(src, dst)
+
+ case majorTypeMap:
+ map2Json(src, dst)
+
+ case majorTypeTags:
+ s := decodeTagData(src)
+ dst.Write(s)
+
+ case majorTypeSimpleAndFloat:
+ s := decodeSimpleFloat(src)
+ dst.Write(s)
+ }
+}
+
+func moreBytesToRead(src *bufio.Reader) bool {
+ _, e := src.ReadByte()
+ if e == nil {
+ src.UnreadByte()
+ return true
+ }
+ return false
+}
+
+// Cbor2JsonManyObjects decodes all the CBOR Objects read from src
+// reader. It keeps on decoding until reader returns EOF (error when reading).
+// Decoded string is written to the dst. At the end of every CBOR Object
+// newline is written to the output stream.
+//
+// Returns error (if any) that was encountered during decode.
+// The child functions will generate a panic when error is encountered and
+// this function will recover non-runtime Errors and return the reason as error.
+func Cbor2JsonManyObjects(src io.Reader, dst io.Writer) (err error) {
+ defer func() {
+ if r := recover(); r != nil {
+ if _, ok := r.(runtime.Error); ok {
+ panic(r)
+ }
+ err = r.(error)
+ }
+ }()
+ bufRdr := bufio.NewReader(src)
+ for moreBytesToRead(bufRdr) {
+ cbor2JsonOneObject(bufRdr, dst)
+ dst.Write([]byte("\n"))
+ }
+ return nil
+}
+
+// Detect if the bytes to be printed is Binary or not.
+func binaryFmt(p []byte) bool {
+ if len(p) > 0 && p[0] > 0x7F {
+ return true
+ }
+ return false
+}
+
+func getReader(str string) *bufio.Reader {
+ return bufio.NewReader(strings.NewReader(str))
+}
+
+// DecodeIfBinaryToString converts a binary formatted log msg to a
+// JSON formatted String Log message - suitable for printing to Console/Syslog.
+func DecodeIfBinaryToString(in []byte) string {
+ if binaryFmt(in) {
+ var b bytes.Buffer
+ Cbor2JsonManyObjects(strings.NewReader(string(in)), &b)
+ return b.String()
+ }
+ return string(in)
+}
+
+// DecodeObjectToStr checks if the input is a binary format, if so,
+// it will decode a single Object and return the decoded string.
+func DecodeObjectToStr(in []byte) string {
+ if binaryFmt(in) {
+ var b bytes.Buffer
+ cbor2JsonOneObject(getReader(string(in)), &b)
+ return b.String()
+ }
+ return string(in)
+}
+
+// DecodeIfBinaryToBytes checks if the input is a binary format, if so,
+// it will decode all Objects and return the decoded string as byte array.
+func DecodeIfBinaryToBytes(in []byte) []byte {
+ if binaryFmt(in) {
+ var b bytes.Buffer
+ Cbor2JsonManyObjects(bytes.NewReader(in), &b)
+ return b.Bytes()
+ }
+ return in
+}
diff --git a/vendor/github.com/rs/zerolog/internal/cbor/string.go b/vendor/github.com/rs/zerolog/internal/cbor/string.go
new file mode 100644
index 00000000..9fc9a4f8
--- /dev/null
+++ b/vendor/github.com/rs/zerolog/internal/cbor/string.go
@@ -0,0 +1,117 @@
+package cbor
+
+import "fmt"
+
+// AppendStrings encodes and adds an array of strings to the dst byte array.
+func (e Encoder) AppendStrings(dst []byte, vals []string) []byte {
+ major := majorTypeArray
+ l := len(vals)
+ if l <= additionalMax {
+ lb := byte(l)
+ dst = append(dst, major|lb)
+ } else {
+ dst = appendCborTypePrefix(dst, major, uint64(l))
+ }
+ for _, v := range vals {
+ dst = e.AppendString(dst, v)
+ }
+ return dst
+}
+
+// AppendString encodes and adds a string to the dst byte array.
+func (Encoder) AppendString(dst []byte, s string) []byte {
+ major := majorTypeUtf8String
+
+ l := len(s)
+ if l <= additionalMax {
+ lb := byte(l)
+ dst = append(dst, major|lb)
+ } else {
+ dst = appendCborTypePrefix(dst, majorTypeUtf8String, uint64(l))
+ }
+ return append(dst, s...)
+}
+
+// AppendStringers encodes and adds an array of Stringer values
+// to the dst byte array.
+func (e Encoder) AppendStringers(dst []byte, vals []fmt.Stringer) []byte {
+ if len(vals) == 0 {
+ return e.AppendArrayEnd(e.AppendArrayStart(dst))
+ }
+ dst = e.AppendArrayStart(dst)
+ dst = e.AppendStringer(dst, vals[0])
+ if len(vals) > 1 {
+ for _, val := range vals[1:] {
+ dst = e.AppendStringer(dst, val)
+ }
+ }
+ return e.AppendArrayEnd(dst)
+}
+
+// AppendStringer encodes and adds the Stringer value to the dst
+// byte array.
+func (e Encoder) AppendStringer(dst []byte, val fmt.Stringer) []byte {
+ if val == nil {
+ return e.AppendNil(dst)
+ }
+ return e.AppendString(dst, val.String())
+}
+
+// AppendBytes encodes and adds an array of bytes to the dst byte array.
+func (Encoder) AppendBytes(dst, s []byte) []byte {
+ major := majorTypeByteString
+
+ l := len(s)
+ if l <= additionalMax {
+ lb := byte(l)
+ dst = append(dst, major|lb)
+ } else {
+ dst = appendCborTypePrefix(dst, major, uint64(l))
+ }
+ return append(dst, s...)
+}
+
+// AppendEmbeddedJSON adds a tag and embeds input JSON as such.
+func AppendEmbeddedJSON(dst, s []byte) []byte {
+ major := majorTypeTags
+ minor := additionalTypeEmbeddedJSON
+
+ // Append the TAG to indicate this is Embedded JSON.
+ dst = append(dst, major|additionalTypeIntUint16)
+ dst = append(dst, byte(minor>>8))
+ dst = append(dst, byte(minor&0xff))
+
+ // Append the JSON Object as Byte String.
+ major = majorTypeByteString
+
+ l := len(s)
+ if l <= additionalMax {
+ lb := byte(l)
+ dst = append(dst, major|lb)
+ } else {
+ dst = appendCborTypePrefix(dst, major, uint64(l))
+ }
+ return append(dst, s...)
+}
+
+// AppendEmbeddedCBOR adds a tag and embeds input CBOR as such.
+func AppendEmbeddedCBOR(dst, s []byte) []byte {
+ major := majorTypeTags
+ minor := additionalTypeEmbeddedCBOR
+
+ // Append the TAG to indicate this is Embedded JSON.
+ dst = append(dst, major|additionalTypeIntUint8)
+ dst = append(dst, minor)
+
+ // Append the CBOR Object as Byte String.
+ major = majorTypeByteString
+
+ l := len(s)
+ if l <= additionalMax {
+ lb := byte(l)
+ dst = append(dst, major|lb)
+ } else {
+ dst = appendCborTypePrefix(dst, major, uint64(l))
+ }
+ return append(dst, s...)
+}
diff --git a/vendor/github.com/rs/zerolog/internal/cbor/time.go b/vendor/github.com/rs/zerolog/internal/cbor/time.go
new file mode 100644
index 00000000..7c0eccee
--- /dev/null
+++ b/vendor/github.com/rs/zerolog/internal/cbor/time.go
@@ -0,0 +1,93 @@
+package cbor
+
+import (
+ "time"
+)
+
+func appendIntegerTimestamp(dst []byte, t time.Time) []byte {
+ major := majorTypeTags
+ minor := additionalTypeTimestamp
+ dst = append(dst, major|minor)
+ secs := t.Unix()
+ var val uint64
+ if secs < 0 {
+ major = majorTypeNegativeInt
+ val = uint64(-secs - 1)
+ } else {
+ major = majorTypeUnsignedInt
+ val = uint64(secs)
+ }
+ dst = appendCborTypePrefix(dst, major, val)
+ return dst
+}
+
+func (e Encoder) appendFloatTimestamp(dst []byte, t time.Time) []byte {
+ major := majorTypeTags
+ minor := additionalTypeTimestamp
+ dst = append(dst, major|minor)
+ secs := t.Unix()
+ nanos := t.Nanosecond()
+ var val float64
+ val = float64(secs)*1.0 + float64(nanos)*1e-9
+ return e.AppendFloat64(dst, val, -1)
+}
+
+// AppendTime encodes and adds a timestamp to the dst byte array.
+func (e Encoder) AppendTime(dst []byte, t time.Time, unused string) []byte {
+ utc := t.UTC()
+ if utc.Nanosecond() == 0 {
+ return appendIntegerTimestamp(dst, utc)
+ }
+ return e.appendFloatTimestamp(dst, utc)
+}
+
+// AppendTimes encodes and adds an array of timestamps to the dst byte array.
+func (e Encoder) AppendTimes(dst []byte, vals []time.Time, unused string) []byte {
+ major := majorTypeArray
+ l := len(vals)
+ if l == 0 {
+ return e.AppendArrayEnd(e.AppendArrayStart(dst))
+ }
+ if l <= additionalMax {
+ lb := byte(l)
+ dst = append(dst, major|lb)
+ } else {
+ dst = appendCborTypePrefix(dst, major, uint64(l))
+ }
+
+ for _, t := range vals {
+ dst = e.AppendTime(dst, t, unused)
+ }
+ return dst
+}
+
+// AppendDuration encodes and adds a duration to the dst byte array.
+// useInt field indicates whether to store the duration as seconds (integer) or
+// as seconds+nanoseconds (float).
+func (e Encoder) AppendDuration(dst []byte, d time.Duration, unit time.Duration, useInt bool, unused int) []byte {
+ if useInt {
+ return e.AppendInt64(dst, int64(d/unit))
+ }
+ return e.AppendFloat64(dst, float64(d)/float64(unit), unused)
+}
+
+// AppendDurations encodes and adds an array of durations to the dst byte array.
+// useInt field indicates whether to store the duration as seconds (integer) or
+// as seconds+nanoseconds (float).
+func (e Encoder) AppendDurations(dst []byte, vals []time.Duration, unit time.Duration, useInt bool, unused int) []byte {
+ major := majorTypeArray
+ l := len(vals)
+ if l == 0 {
+ return e.AppendArrayEnd(e.AppendArrayStart(dst))
+ }
+ if l <= additionalMax {
+ lb := byte(l)
+ dst = append(dst, major|lb)
+ } else {
+ dst = appendCborTypePrefix(dst, major, uint64(l))
+ }
+ for _, d := range vals {
+ dst = e.AppendDuration(dst, d, unit, useInt, unused)
+ }
+ return dst
+}
diff --git a/vendor/github.com/rs/zerolog/internal/cbor/types.go b/vendor/github.com/rs/zerolog/internal/cbor/types.go
new file mode 100644
index 00000000..454d68bd
--- /dev/null
+++ b/vendor/github.com/rs/zerolog/internal/cbor/types.go
@@ -0,0 +1,486 @@
+package cbor
+
+import (
+ "fmt"
+ "math"
+ "net"
+ "reflect"
+)
+
+// AppendNil inserts a 'Nil' object into the dst byte array.
+func (Encoder) AppendNil(dst []byte) []byte {
+ return append(dst, majorTypeSimpleAndFloat|additionalTypeNull)
+}
+
+// AppendBeginMarker inserts a map start into the dst byte array.
+func (Encoder) AppendBeginMarker(dst []byte) []byte {
+ return append(dst, majorTypeMap|additionalTypeInfiniteCount)
+}
+
+// AppendEndMarker inserts a map end into the dst byte array.
+func (Encoder) AppendEndMarker(dst []byte) []byte {
+ return append(dst, majorTypeSimpleAndFloat|additionalTypeBreak)
+}
+
+// AppendObjectData takes an object in form of a byte array and appends to dst.
+func (Encoder) AppendObjectData(dst []byte, o []byte) []byte {
+ // BeginMarker is present in the dst, which
+ // should not be copied when appending to existing data.
+ return append(dst, o[1:]...)
+}
+
+// AppendArrayStart adds markers to indicate the start of an array.
+func (Encoder) AppendArrayStart(dst []byte) []byte {
+ return append(dst, majorTypeArray|additionalTypeInfiniteCount)
+}
+
+// AppendArrayEnd adds markers to indicate the end of an array.
+func (Encoder) AppendArrayEnd(dst []byte) []byte {
+ return append(dst, majorTypeSimpleAndFloat|additionalTypeBreak)
+}
+
+// AppendArrayDelim adds markers to indicate end of a particular array element.
+func (Encoder) AppendArrayDelim(dst []byte) []byte {
+ //No delimiters needed in cbor
+ return dst
+}
+
+// AppendLineBreak is a noop that keep API compat with json encoder.
+func (Encoder) AppendLineBreak(dst []byte) []byte {
+ // No line breaks needed in binary format.
+ return dst
+}
+
+// AppendBool encodes and inserts a boolean value into the dst byte array.
+func (Encoder) AppendBool(dst []byte, val bool) []byte {
+ b := additionalTypeBoolFalse
+ if val {
+ b = additionalTypeBoolTrue
+ }
+ return append(dst, majorTypeSimpleAndFloat|b)
+}
+
+// AppendBools encodes and inserts an array of boolean values into the dst byte array.
+func (e Encoder) AppendBools(dst []byte, vals []bool) []byte {
+ major := majorTypeArray
+ l := len(vals)
+ if l == 0 {
+ return e.AppendArrayEnd(e.AppendArrayStart(dst))
+ }
+ if l <= additionalMax {
+ lb := byte(l)
+ dst = append(dst, major|lb)
+ } else {
+ dst = appendCborTypePrefix(dst, major, uint64(l))
+ }
+ for _, v := range vals {
+ dst = e.AppendBool(dst, v)
+ }
+ return dst
+}
+
+// AppendInt encodes and inserts an integer value into the dst byte array.
+func (Encoder) AppendInt(dst []byte, val int) []byte {
+ major := majorTypeUnsignedInt
+ contentVal := val
+ if val < 0 {
+ major = majorTypeNegativeInt
+ contentVal = -val - 1
+ }
+ if contentVal <= additionalMax {
+ lb := byte(contentVal)
+ dst = append(dst, major|lb)
+ } else {
+ dst = appendCborTypePrefix(dst, major, uint64(contentVal))
+ }
+ return dst
+}
+
+// AppendInts encodes and inserts an array of integer values into the dst byte array.
+func (e Encoder) AppendInts(dst []byte, vals []int) []byte {
+ major := majorTypeArray
+ l := len(vals)
+ if l == 0 {
+ return e.AppendArrayEnd(e.AppendArrayStart(dst))
+ }
+ if l <= additionalMax {
+ lb := byte(l)
+ dst = append(dst, major|lb)
+ } else {
+ dst = appendCborTypePrefix(dst, major, uint64(l))
+ }
+ for _, v := range vals {
+ dst = e.AppendInt(dst, v)
+ }
+ return dst
+}
+
+// AppendInt8 encodes and inserts an int8 value into the dst byte array.
+func (e Encoder) AppendInt8(dst []byte, val int8) []byte {
+ return e.AppendInt(dst, int(val))
+}
+
+// AppendInts8 encodes and inserts an array of integer values into the dst byte array.
+func (e Encoder) AppendInts8(dst []byte, vals []int8) []byte {
+ major := majorTypeArray
+ l := len(vals)
+ if l == 0 {
+ return e.AppendArrayEnd(e.AppendArrayStart(dst))
+ }
+ if l <= additionalMax {
+ lb := byte(l)
+ dst = append(dst, major|lb)
+ } else {
+ dst = appendCborTypePrefix(dst, major, uint64(l))
+ }
+ for _, v := range vals {
+ dst = e.AppendInt(dst, int(v))
+ }
+ return dst
+}
+
+// AppendInt16 encodes and inserts a int16 value into the dst byte array.
+func (e Encoder) AppendInt16(dst []byte, val int16) []byte {
+ return e.AppendInt(dst, int(val))
+}
+
+// AppendInts16 encodes and inserts an array of int16 values into the dst byte array.
+func (e Encoder) AppendInts16(dst []byte, vals []int16) []byte {
+ major := majorTypeArray
+ l := len(vals)
+ if l == 0 {
+ return e.AppendArrayEnd(e.AppendArrayStart(dst))
+ }
+ if l <= additionalMax {
+ lb := byte(l)
+ dst = append(dst, major|lb)
+ } else {
+ dst = appendCborTypePrefix(dst, major, uint64(l))
+ }
+ for _, v := range vals {
+ dst = e.AppendInt(dst, int(v))
+ }
+ return dst
+}
+
+// AppendInt32 encodes and inserts a int32 value into the dst byte array.
+func (e Encoder) AppendInt32(dst []byte, val int32) []byte {
+ return e.AppendInt(dst, int(val))
+}
+
+// AppendInts32 encodes and inserts an array of int32 values into the dst byte array.
+func (e Encoder) AppendInts32(dst []byte, vals []int32) []byte {
+ major := majorTypeArray
+ l := len(vals)
+ if l == 0 {
+ return e.AppendArrayEnd(e.AppendArrayStart(dst))
+ }
+ if l <= additionalMax {
+ lb := byte(l)
+ dst = append(dst, major|lb)
+ } else {
+ dst = appendCborTypePrefix(dst, major, uint64(l))
+ }
+ for _, v := range vals {
+ dst = e.AppendInt(dst, int(v))
+ }
+ return dst
+}
+
+// AppendInt64 encodes and inserts a int64 value into the dst byte array.
+func (Encoder) AppendInt64(dst []byte, val int64) []byte {
+ major := majorTypeUnsignedInt
+ contentVal := val
+ if val < 0 {
+ major = majorTypeNegativeInt
+ contentVal = -val - 1
+ }
+ if contentVal <= additionalMax {
+ lb := byte(contentVal)
+ dst = append(dst, major|lb)
+ } else {
+ dst = appendCborTypePrefix(dst, major, uint64(contentVal))
+ }
+ return dst
+}
+
+// AppendInts64 encodes and inserts an array of int64 values into the dst byte array.
+func (e Encoder) AppendInts64(dst []byte, vals []int64) []byte {
+ major := majorTypeArray
+ l := len(vals)
+ if l == 0 {
+ return e.AppendArrayEnd(e.AppendArrayStart(dst))
+ }
+ if l <= additionalMax {
+ lb := byte(l)
+ dst = append(dst, major|lb)
+ } else {
+ dst = appendCborTypePrefix(dst, major, uint64(l))
+ }
+ for _, v := range vals {
+ dst = e.AppendInt64(dst, v)
+ }
+ return dst
+}
+
+// AppendUint encodes and inserts an unsigned integer value into the dst byte array.
+func (e Encoder) AppendUint(dst []byte, val uint) []byte {
+ return e.AppendInt64(dst, int64(val))
+}
+
+// AppendUints encodes and inserts an array of unsigned integer values into the dst byte array.
+func (e Encoder) AppendUints(dst []byte, vals []uint) []byte {
+ major := majorTypeArray
+ l := len(vals)
+ if l == 0 {
+ return e.AppendArrayEnd(e.AppendArrayStart(dst))
+ }
+ if l <= additionalMax {
+ lb := byte(l)
+ dst = append(dst, major|lb)
+ } else {
+ dst = appendCborTypePrefix(dst, major, uint64(l))
+ }
+ for _, v := range vals {
+ dst = e.AppendUint(dst, v)
+ }
+ return dst
+}
+
+// AppendUint8 encodes and inserts a unsigned int8 value into the dst byte array.
+func (e Encoder) AppendUint8(dst []byte, val uint8) []byte {
+ return e.AppendUint(dst, uint(val))
+}
+
+// AppendUints8 encodes and inserts an array of uint8 values into the dst byte array.
+func (e Encoder) AppendUints8(dst []byte, vals []uint8) []byte {
+ major := majorTypeArray
+ l := len(vals)
+ if l == 0 {
+ return e.AppendArrayEnd(e.AppendArrayStart(dst))
+ }
+ if l <= additionalMax {
+ lb := byte(l)
+ dst = append(dst, major|lb)
+ } else {
+ dst = appendCborTypePrefix(dst, major, uint64(l))
+ }
+ for _, v := range vals {
+ dst = e.AppendUint8(dst, v)
+ }
+ return dst
+}
+
+// AppendUint16 encodes and inserts a uint16 value into the dst byte array.
+func (e Encoder) AppendUint16(dst []byte, val uint16) []byte {
+ return e.AppendUint(dst, uint(val))
+}
+
+// AppendUints16 encodes and inserts an array of uint16 values into the dst byte array.
+func (e Encoder) AppendUints16(dst []byte, vals []uint16) []byte {
+ major := majorTypeArray
+ l := len(vals)
+ if l == 0 {
+ return e.AppendArrayEnd(e.AppendArrayStart(dst))
+ }
+ if l <= additionalMax {
+ lb := byte(l)
+ dst = append(dst, major|lb)
+ } else {
+ dst = appendCborTypePrefix(dst, major, uint64(l))
+ }
+ for _, v := range vals {
+ dst = e.AppendUint16(dst, v)
+ }
+ return dst
+}
+
+// AppendUint32 encodes and inserts a uint32 value into the dst byte array.
+func (e Encoder) AppendUint32(dst []byte, val uint32) []byte {
+ return e.AppendUint(dst, uint(val))
+}
+
+// AppendUints32 encodes and inserts an array of uint32 values into the dst byte array.
+func (e Encoder) AppendUints32(dst []byte, vals []uint32) []byte {
+ major := majorTypeArray
+ l := len(vals)
+ if l == 0 {
+ return e.AppendArrayEnd(e.AppendArrayStart(dst))
+ }
+ if l <= additionalMax {
+ lb := byte(l)
+ dst = append(dst, major|lb)
+ } else {
+ dst = appendCborTypePrefix(dst, major, uint64(l))
+ }
+ for _, v := range vals {
+ dst = e.AppendUint32(dst, v)
+ }
+ return dst
+}
+
+// AppendUint64 encodes and inserts a uint64 value into the dst byte array.
+func (Encoder) AppendUint64(dst []byte, val uint64) []byte {
+ major := majorTypeUnsignedInt
+ contentVal := val
+ if contentVal <= additionalMax {
+ lb := byte(contentVal)
+ dst = append(dst, major|lb)
+ } else {
+ dst = appendCborTypePrefix(dst, major, contentVal)
+ }
+ return dst
+}
+
+// AppendUints64 encodes and inserts an array of uint64 values into the dst byte array.
+func (e Encoder) AppendUints64(dst []byte, vals []uint64) []byte {
+ major := majorTypeArray
+ l := len(vals)
+ if l == 0 {
+ return e.AppendArrayEnd(e.AppendArrayStart(dst))
+ }
+ if l <= additionalMax {
+ lb := byte(l)
+ dst = append(dst, major|lb)
+ } else {
+ dst = appendCborTypePrefix(dst, major, uint64(l))
+ }
+ for _, v := range vals {
+ dst = e.AppendUint64(dst, v)
+ }
+ return dst
+}
+
+// AppendFloat32 encodes and inserts a single precision float value into the dst byte array.
+func (Encoder) AppendFloat32(dst []byte, val float32, unused int) []byte {
+ switch {
+ case math.IsNaN(float64(val)):
+ return append(dst, "\xfa\x7f\xc0\x00\x00"...)
+ case math.IsInf(float64(val), 1):
+ return append(dst, "\xfa\x7f\x80\x00\x00"...)
+ case math.IsInf(float64(val), -1):
+ return append(dst, "\xfa\xff\x80\x00\x00"...)
+ }
+ major := majorTypeSimpleAndFloat
+ subType := additionalTypeFloat32
+ n := math.Float32bits(val)
+ var buf [4]byte
+ for i := uint(0); i < 4; i++ {
+ buf[i] = byte(n >> ((3 - i) * 8))
+ }
+ return append(append(dst, major|subType), buf[0], buf[1], buf[2], buf[3])
+}
+
+// AppendFloats32 encodes and inserts an array of single precision float value into the dst byte array.
+func (e Encoder) AppendFloats32(dst []byte, vals []float32, unused int) []byte {
+ major := majorTypeArray
+ l := len(vals)
+ if l == 0 {
+ return e.AppendArrayEnd(e.AppendArrayStart(dst))
+ }
+ if l <= additionalMax {
+ lb := byte(l)
+ dst = append(dst, major|lb)
+ } else {
+ dst = appendCborTypePrefix(dst, major, uint64(l))
+ }
+ for _, v := range vals {
+ dst = e.AppendFloat32(dst, v, unused)
+ }
+ return dst
+}
+
+// AppendFloat64 encodes and inserts a double precision float value into the dst byte array.
+func (Encoder) AppendFloat64(dst []byte, val float64, unused int) []byte {
+ switch {
+ case math.IsNaN(val):
+ return append(dst, "\xfb\x7f\xf8\x00\x00\x00\x00\x00\x00"...)
+ case math.IsInf(val, 1):
+ return append(dst, "\xfb\x7f\xf0\x00\x00\x00\x00\x00\x00"...)
+ case math.IsInf(val, -1):
+ return append(dst, "\xfb\xff\xf0\x00\x00\x00\x00\x00\x00"...)
+ }
+ major := majorTypeSimpleAndFloat
+ subType := additionalTypeFloat64
+ n := math.Float64bits(val)
+ dst = append(dst, major|subType)
+ for i := uint(1); i <= 8; i++ {
+ b := byte(n >> ((8 - i) * 8))
+ dst = append(dst, b)
+ }
+ return dst
+}
+
+// AppendFloats64 encodes and inserts an array of double precision float values into the dst byte array.
+func (e Encoder) AppendFloats64(dst []byte, vals []float64, unused int) []byte {
+ major := majorTypeArray
+ l := len(vals)
+ if l == 0 {
+ return e.AppendArrayEnd(e.AppendArrayStart(dst))
+ }
+ if l <= additionalMax {
+ lb := byte(l)
+ dst = append(dst, major|lb)
+ } else {
+ dst = appendCborTypePrefix(dst, major, uint64(l))
+ }
+ for _, v := range vals {
+ dst = e.AppendFloat64(dst, v, unused)
+ }
+ return dst
+}
+
+// AppendInterface takes an arbitrary object and converts it to JSON and embeds it dst.
+func (e Encoder) AppendInterface(dst []byte, i interface{}) []byte {
+ marshaled, err := JSONMarshalFunc(i)
+ if err != nil {
+ return e.AppendString(dst, fmt.Sprintf("marshaling error: %v", err))
+ }
+ return AppendEmbeddedJSON(dst, marshaled)
+}
+
+// AppendType appends the parameter type (as a string) to the input byte slice.
+func (e Encoder) AppendType(dst []byte, i interface{}) []byte {
+ if i == nil {
+ return e.AppendString(dst, "")
+ }
+ return e.AppendString(dst, reflect.TypeOf(i).String())
+}
+
+// AppendIPAddr encodes and inserts an IP Address (IPv4 or IPv6).
+func (e Encoder) AppendIPAddr(dst []byte, ip net.IP) []byte {
+ dst = append(dst, majorTypeTags|additionalTypeIntUint16)
+ dst = append(dst, byte(additionalTypeTagNetworkAddr>>8))
+ dst = append(dst, byte(additionalTypeTagNetworkAddr&0xff))
+ return e.AppendBytes(dst, ip)
+}
+
+// AppendIPPrefix encodes and inserts an IP Address Prefix (Address + Mask Length).
+func (e Encoder) AppendIPPrefix(dst []byte, pfx net.IPNet) []byte {
+ dst = append(dst, majorTypeTags|additionalTypeIntUint16)
+ dst = append(dst, byte(additionalTypeTagNetworkPrefix>>8))
+ dst = append(dst, byte(additionalTypeTagNetworkPrefix&0xff))
+
+ // Prefix is a tuple (aka MAP of 1 pair of elements) -
+ // first element is prefix, second is mask length.
+ dst = append(dst, majorTypeMap|0x1)
+ dst = e.AppendBytes(dst, pfx.IP)
+ maskLen, _ := pfx.Mask.Size()
+ return e.AppendUint8(dst, uint8(maskLen))
+}
+
+// AppendMACAddr encodes and inserts a Hardware (MAC) address.
+func (e Encoder) AppendMACAddr(dst []byte, ha net.HardwareAddr) []byte {
+ dst = append(dst, majorTypeTags|additionalTypeIntUint16)
+ dst = append(dst, byte(additionalTypeTagNetworkAddr>>8))
+ dst = append(dst, byte(additionalTypeTagNetworkAddr&0xff))
+ return e.AppendBytes(dst, ha)
+}
+
+// AppendHex adds a TAG and inserts a hex bytes as a string.
+func (e Encoder) AppendHex(dst []byte, val []byte) []byte {
+ dst = append(dst, majorTypeTags|additionalTypeIntUint16)
+ dst = append(dst, byte(additionalTypeTagHexString>>8))
+ dst = append(dst, byte(additionalTypeTagHexString&0xff))
+ return e.AppendBytes(dst, val)
+}
diff --git a/vendor/github.com/rs/zerolog/internal/json/base.go b/vendor/github.com/rs/zerolog/internal/json/base.go
new file mode 100644
index 00000000..09ec59f4
--- /dev/null
+++ b/vendor/github.com/rs/zerolog/internal/json/base.go
@@ -0,0 +1,19 @@
+package json
+
+// JSONMarshalFunc is used to marshal interface to JSON encoded byte slice.
+// Making it package level instead of embedded in Encoder brings
+// some extra efforts at importing, but avoids value copy when the functions
+// of Encoder being invoked.
+// DO REMEMBER to set this variable at importing, or
+// you might get a nil pointer dereference panic at runtime.
+var JSONMarshalFunc func(v interface{}) ([]byte, error)
+
+type Encoder struct{}
+
+// AppendKey appends a new key to the output JSON.
+func (e Encoder) AppendKey(dst []byte, key string) []byte {
+ if dst[len(dst)-1] != '{' {
+ dst = append(dst, ',')
+ }
+ return append(e.AppendString(dst, key), ':')
+}
diff --git a/vendor/github.com/rs/zerolog/internal/json/bytes.go b/vendor/github.com/rs/zerolog/internal/json/bytes.go
new file mode 100644
index 00000000..de64120d
--- /dev/null
+++ b/vendor/github.com/rs/zerolog/internal/json/bytes.go
@@ -0,0 +1,85 @@
+package json
+
+import "unicode/utf8"
+
+// AppendBytes is a mirror of appendString with []byte arg
+func (Encoder) AppendBytes(dst, s []byte) []byte {
+ dst = append(dst, '"')
+ for i := 0; i < len(s); i++ {
+ if !noEscapeTable[s[i]] {
+ dst = appendBytesComplex(dst, s, i)
+ return append(dst, '"')
+ }
+ }
+ dst = append(dst, s...)
+ return append(dst, '"')
+}
+
+// AppendHex encodes the input bytes to a hex string and appends
+// the encoded string to the input byte slice.
+//
+// The operation loops though each byte and encodes it as hex using
+// the hex lookup table.
+func (Encoder) AppendHex(dst, s []byte) []byte {
+ dst = append(dst, '"')
+ for _, v := range s {
+ dst = append(dst, hex[v>>4], hex[v&0x0f])
+ }
+ return append(dst, '"')
+}
+
+// appendBytesComplex is a mirror of the appendStringComplex
+// with []byte arg
+func appendBytesComplex(dst, s []byte, i int) []byte {
+ start := 0
+ for i < len(s) {
+ b := s[i]
+ if b >= utf8.RuneSelf {
+ r, size := utf8.DecodeRune(s[i:])
+ if r == utf8.RuneError && size == 1 {
+ if start < i {
+ dst = append(dst, s[start:i]...)
+ }
+ dst = append(dst, `\ufffd`...)
+ i += size
+ start = i
+ continue
+ }
+ i += size
+ continue
+ }
+ if noEscapeTable[b] {
+ i++
+ continue
+ }
+ // We encountered a character that needs to be encoded.
+ // Let's append the previous simple characters to the byte slice
+ // and switch our operation to read and encode the remainder
+ // characters byte-by-byte.
+ if start < i {
+ dst = append(dst, s[start:i]...)
+ }
+ switch b {
+ case '"', '\\':
+ dst = append(dst, '\\', b)
+ case '\b':
+ dst = append(dst, '\\', 'b')
+ case '\f':
+ dst = append(dst, '\\', 'f')
+ case '\n':
+ dst = append(dst, '\\', 'n')
+ case '\r':
+ dst = append(dst, '\\', 'r')
+ case '\t':
+ dst = append(dst, '\\', 't')
+ default:
+ dst = append(dst, '\\', 'u', '0', '0', hex[b>>4], hex[b&0xF])
+ }
+ i++
+ start = i
+ }
+ if start < len(s) {
+ dst = append(dst, s[start:]...)
+ }
+ return dst
+}
diff --git a/vendor/github.com/rs/zerolog/internal/json/string.go b/vendor/github.com/rs/zerolog/internal/json/string.go
new file mode 100644
index 00000000..fd7770f2
--- /dev/null
+++ b/vendor/github.com/rs/zerolog/internal/json/string.go
@@ -0,0 +1,149 @@
+package json
+
+import (
+ "fmt"
+ "unicode/utf8"
+)
+
+const hex = "0123456789abcdef"
+
+var noEscapeTable = [256]bool{}
+
+func init() {
+ for i := 0; i <= 0x7e; i++ {
+ noEscapeTable[i] = i >= 0x20 && i != '\\' && i != '"'
+ }
+}
+
+// AppendStrings encodes the input strings to json and
+// appends the encoded string list to the input byte slice.
+func (e Encoder) AppendStrings(dst []byte, vals []string) []byte {
+ if len(vals) == 0 {
+ return append(dst, '[', ']')
+ }
+ dst = append(dst, '[')
+ dst = e.AppendString(dst, vals[0])
+ if len(vals) > 1 {
+ for _, val := range vals[1:] {
+ dst = e.AppendString(append(dst, ','), val)
+ }
+ }
+ dst = append(dst, ']')
+ return dst
+}
+
+// AppendString encodes the input string to json and appends
+// the encoded string to the input byte slice.
+//
+// The operation loops though each byte in the string looking
+// for characters that need json or utf8 encoding. If the string
+// does not need encoding, then the string is appended in its
+// entirety to the byte slice.
+// If we encounter a byte that does need encoding, switch up
+// the operation and perform a byte-by-byte read-encode-append.
+func (Encoder) AppendString(dst []byte, s string) []byte {
+ // Start with a double quote.
+ dst = append(dst, '"')
+ // Loop through each character in the string.
+ for i := 0; i < len(s); i++ {
+ // Check if the character needs encoding. Control characters, slashes,
+ // and the double quote need json encoding. Bytes above the ascii
+ // boundary needs utf8 encoding.
+ if !noEscapeTable[s[i]] {
+ // We encountered a character that needs to be encoded. Switch
+ // to complex version of the algorithm.
+ dst = appendStringComplex(dst, s, i)
+ return append(dst, '"')
+ }
+ }
+ // The string has no need for encoding and therefore is directly
+ // appended to the byte slice.
+ dst = append(dst, s...)
+ // End with a double quote
+ return append(dst, '"')
+}
+
+// AppendStringers encodes the provided Stringer list to json and
+// appends the encoded Stringer list to the input byte slice.
+func (e Encoder) AppendStringers(dst []byte, vals []fmt.Stringer) []byte {
+ if len(vals) == 0 {
+ return append(dst, '[', ']')
+ }
+ dst = append(dst, '[')
+ dst = e.AppendStringer(dst, vals[0])
+ if len(vals) > 1 {
+ for _, val := range vals[1:] {
+ dst = e.AppendStringer(append(dst, ','), val)
+ }
+ }
+ return append(dst, ']')
+}
+
+// AppendStringer encodes the input Stringer to json and appends the
+// encoded Stringer value to the input byte slice.
+func (e Encoder) AppendStringer(dst []byte, val fmt.Stringer) []byte {
+ if val == nil {
+ return e.AppendInterface(dst, nil)
+ }
+ return e.AppendString(dst, val.String())
+}
+
+//// appendStringComplex is used by appendString to take over an in
+// progress JSON string encoding that encountered a character that needs
+// to be encoded.
+func appendStringComplex(dst []byte, s string, i int) []byte {
+ start := 0
+ for i < len(s) {
+ b := s[i]
+ if b >= utf8.RuneSelf {
+ r, size := utf8.DecodeRuneInString(s[i:])
+ if r == utf8.RuneError && size == 1 {
+ // In case of error, first append previous simple characters to
+ // the byte slice if any and append a replacement character code
+ // in place of the invalid sequence.
+ if start < i {
+ dst = append(dst, s[start:i]...)
+ }
+ dst = append(dst, `\ufffd`...)
+ i += size
+ start = i
+ continue
+ }
+ i += size
+ continue
+ }
+ if noEscapeTable[b] {
+ i++
+ continue
+ }
+ // We encountered a character that needs to be encoded.
+ // Let's append the previous simple characters to the byte slice
+ // and switch our operation to read and encode the remainder
+ // characters byte-by-byte.
+ if start < i {
+ dst = append(dst, s[start:i]...)
+ }
+ switch b {
+ case '"', '\\':
+ dst = append(dst, '\\', b)
+ case '\b':
+ dst = append(dst, '\\', 'b')
+ case '\f':
+ dst = append(dst, '\\', 'f')
+ case '\n':
+ dst = append(dst, '\\', 'n')
+ case '\r':
+ dst = append(dst, '\\', 'r')
+ case '\t':
+ dst = append(dst, '\\', 't')
+ default:
+ dst = append(dst, '\\', 'u', '0', '0', hex[b>>4], hex[b&0xF])
+ }
+ i++
+ start = i
+ }
+ if start < len(s) {
+ dst = append(dst, s[start:]...)
+ }
+ return dst
+}
diff --git a/vendor/github.com/rs/zerolog/internal/json/time.go b/vendor/github.com/rs/zerolog/internal/json/time.go
new file mode 100644
index 00000000..08cbbd9b
--- /dev/null
+++ b/vendor/github.com/rs/zerolog/internal/json/time.go
@@ -0,0 +1,113 @@
+package json
+
+import (
+ "strconv"
+ "time"
+)
+
+const (
+ // Import from zerolog/global.go
+ timeFormatUnix = ""
+ timeFormatUnixMs = "UNIXMS"
+ timeFormatUnixMicro = "UNIXMICRO"
+ timeFormatUnixNano = "UNIXNANO"
+)
+
+// AppendTime formats the input time with the given format
+// and appends the encoded string to the input byte slice.
+func (e Encoder) AppendTime(dst []byte, t time.Time, format string) []byte {
+ switch format {
+ case timeFormatUnix:
+ return e.AppendInt64(dst, t.Unix())
+ case timeFormatUnixMs:
+ return e.AppendInt64(dst, t.UnixNano()/1000000)
+ case timeFormatUnixMicro:
+ return e.AppendInt64(dst, t.UnixNano()/1000)
+ case timeFormatUnixNano:
+ return e.AppendInt64(dst, t.UnixNano())
+ }
+ return append(t.AppendFormat(append(dst, '"'), format), '"')
+}
+
+// AppendTimes converts the input times with the given format
+// and appends the encoded string list to the input byte slice.
+func (Encoder) AppendTimes(dst []byte, vals []time.Time, format string) []byte {
+ switch format {
+ case timeFormatUnix:
+ return appendUnixTimes(dst, vals)
+ case timeFormatUnixMs:
+ return appendUnixNanoTimes(dst, vals, 1000000)
+ case timeFormatUnixMicro:
+ return appendUnixNanoTimes(dst, vals, 1000)
+ case timeFormatUnixNano:
+ return appendUnixNanoTimes(dst, vals, 1)
+ }
+ if len(vals) == 0 {
+ return append(dst, '[', ']')
+ }
+ dst = append(dst, '[')
+ dst = append(vals[0].AppendFormat(append(dst, '"'), format), '"')
+ if len(vals) > 1 {
+ for _, t := range vals[1:] {
+ dst = append(t.AppendFormat(append(dst, ',', '"'), format), '"')
+ }
+ }
+ dst = append(dst, ']')
+ return dst
+}
+
+func appendUnixTimes(dst []byte, vals []time.Time) []byte {
+ if len(vals) == 0 {
+ return append(dst, '[', ']')
+ }
+ dst = append(dst, '[')
+ dst = strconv.AppendInt(dst, vals[0].Unix(), 10)
+ if len(vals) > 1 {
+ for _, t := range vals[1:] {
+ dst = strconv.AppendInt(append(dst, ','), t.Unix(), 10)
+ }
+ }
+ dst = append(dst, ']')
+ return dst
+}
+
+func appendUnixNanoTimes(dst []byte, vals []time.Time, div int64) []byte {
+ if len(vals) == 0 {
+ return append(dst, '[', ']')
+ }
+ dst = append(dst, '[')
+ dst = strconv.AppendInt(dst, vals[0].UnixNano()/div, 10)
+ if len(vals) > 1 {
+ for _, t := range vals[1:] {
+ dst = strconv.AppendInt(append(dst, ','), t.UnixNano()/div, 10)
+ }
+ }
+ dst = append(dst, ']')
+ return dst
+}
+
+// AppendDuration formats the input duration with the given unit & format
+// and appends the encoded string to the input byte slice.
+func (e Encoder) AppendDuration(dst []byte, d time.Duration, unit time.Duration, useInt bool, precision int) []byte {
+ if useInt {
+ return strconv.AppendInt(dst, int64(d/unit), 10)
+ }
+ return e.AppendFloat64(dst, float64(d)/float64(unit), precision)
+}
+
+// AppendDurations formats the input durations with the given unit & format
+// and appends the encoded string list to the input byte slice.
+func (e Encoder) AppendDurations(dst []byte, vals []time.Duration, unit time.Duration, useInt bool, precision int) []byte {
+ if len(vals) == 0 {
+ return append(dst, '[', ']')
+ }
+ dst = append(dst, '[')
+ dst = e.AppendDuration(dst, vals[0], unit, useInt, precision)
+ if len(vals) > 1 {
+ for _, d := range vals[1:] {
+ dst = e.AppendDuration(append(dst, ','), d, unit, useInt, precision)
+ }
+ }
+ dst = append(dst, ']')
+ return dst
+}
diff --git a/vendor/github.com/rs/zerolog/internal/json/types.go b/vendor/github.com/rs/zerolog/internal/json/types.go
new file mode 100644
index 00000000..7930491a
--- /dev/null
+++ b/vendor/github.com/rs/zerolog/internal/json/types.go
@@ -0,0 +1,435 @@
+package json
+
+import (
+ "fmt"
+ "math"
+ "net"
+ "reflect"
+ "strconv"
+)
+
+// AppendNil inserts a 'Nil' object into the dst byte array.
+func (Encoder) AppendNil(dst []byte) []byte {
+ return append(dst, "null"...)
+}
+
+// AppendBeginMarker inserts a map start into the dst byte array.
+func (Encoder) AppendBeginMarker(dst []byte) []byte {
+ return append(dst, '{')
+}
+
+// AppendEndMarker inserts a map end into the dst byte array.
+func (Encoder) AppendEndMarker(dst []byte) []byte {
+ return append(dst, '}')
+}
+
+// AppendLineBreak appends a line break.
+func (Encoder) AppendLineBreak(dst []byte) []byte {
+ return append(dst, '\n')
+}
+
+// AppendArrayStart adds markers to indicate the start of an array.
+func (Encoder) AppendArrayStart(dst []byte) []byte {
+ return append(dst, '[')
+}
+
+// AppendArrayEnd adds markers to indicate the end of an array.
+func (Encoder) AppendArrayEnd(dst []byte) []byte {
+ return append(dst, ']')
+}
+
+// AppendArrayDelim adds markers to indicate end of a particular array element.
+func (Encoder) AppendArrayDelim(dst []byte) []byte {
+ if len(dst) > 0 {
+ return append(dst, ',')
+ }
+ return dst
+}
+
+// AppendBool converts the input bool to a string and
+// appends the encoded string to the input byte slice.
+func (Encoder) AppendBool(dst []byte, val bool) []byte {
+ return strconv.AppendBool(dst, val)
+}
+
+// AppendBools encodes the input bools to json and
+// appends the encoded string list to the input byte slice.
+func (Encoder) AppendBools(dst []byte, vals []bool) []byte {
+ if len(vals) == 0 {
+ return append(dst, '[', ']')
+ }
+ dst = append(dst, '[')
+ dst = strconv.AppendBool(dst, vals[0])
+ if len(vals) > 1 {
+ for _, val := range vals[1:] {
+ dst = strconv.AppendBool(append(dst, ','), val)
+ }
+ }
+ dst = append(dst, ']')
+ return dst
+}
+
+// AppendInt converts the input int to a string and
+// appends the encoded string to the input byte slice.
+func (Encoder) AppendInt(dst []byte, val int) []byte {
+ return strconv.AppendInt(dst, int64(val), 10)
+}
+
+// AppendInts encodes the input ints to json and
+// appends the encoded string list to the input byte slice.
+func (Encoder) AppendInts(dst []byte, vals []int) []byte {
+ if len(vals) == 0 {
+ return append(dst, '[', ']')
+ }
+ dst = append(dst, '[')
+ dst = strconv.AppendInt(dst, int64(vals[0]), 10)
+ if len(vals) > 1 {
+ for _, val := range vals[1:] {
+ dst = strconv.AppendInt(append(dst, ','), int64(val), 10)
+ }
+ }
+ dst = append(dst, ']')
+ return dst
+}
+
+// AppendInt8 converts the input []int8 to a string and
+// appends the encoded string to the input byte slice.
+func (Encoder) AppendInt8(dst []byte, val int8) []byte {
+ return strconv.AppendInt(dst, int64(val), 10)
+}
+
+// AppendInts8 encodes the input int8s to json and
+// appends the encoded string list to the input byte slice.
+func (Encoder) AppendInts8(dst []byte, vals []int8) []byte {
+ if len(vals) == 0 {
+ return append(dst, '[', ']')
+ }
+ dst = append(dst, '[')
+ dst = strconv.AppendInt(dst, int64(vals[0]), 10)
+ if len(vals) > 1 {
+ for _, val := range vals[1:] {
+ dst = strconv.AppendInt(append(dst, ','), int64(val), 10)
+ }
+ }
+ dst = append(dst, ']')
+ return dst
+}
+
+// AppendInt16 converts the input int16 to a string and
+// appends the encoded string to the input byte slice.
+func (Encoder) AppendInt16(dst []byte, val int16) []byte {
+ return strconv.AppendInt(dst, int64(val), 10)
+}
+
+// AppendInts16 encodes the input int16s to json and
+// appends the encoded string list to the input byte slice.
+func (Encoder) AppendInts16(dst []byte, vals []int16) []byte {
+ if len(vals) == 0 {
+ return append(dst, '[', ']')
+ }
+ dst = append(dst, '[')
+ dst = strconv.AppendInt(dst, int64(vals[0]), 10)
+ if len(vals) > 1 {
+ for _, val := range vals[1:] {
+ dst = strconv.AppendInt(append(dst, ','), int64(val), 10)
+ }
+ }
+ dst = append(dst, ']')
+ return dst
+}
+
+// AppendInt32 converts the input int32 to a string and
+// appends the encoded string to the input byte slice.
+func (Encoder) AppendInt32(dst []byte, val int32) []byte {
+ return strconv.AppendInt(dst, int64(val), 10)
+}
+
+// AppendInts32 encodes the input int32s to json and
+// appends the encoded string list to the input byte slice.
+func (Encoder) AppendInts32(dst []byte, vals []int32) []byte {
+ if len(vals) == 0 {
+ return append(dst, '[', ']')
+ }
+ dst = append(dst, '[')
+ dst = strconv.AppendInt(dst, int64(vals[0]), 10)
+ if len(vals) > 1 {
+ for _, val := range vals[1:] {
+ dst = strconv.AppendInt(append(dst, ','), int64(val), 10)
+ }
+ }
+ dst = append(dst, ']')
+ return dst
+}
+
+// AppendInt64 converts the input int64 to a string and
+// appends the encoded string to the input byte slice.
+func (Encoder) AppendInt64(dst []byte, val int64) []byte {
+ return strconv.AppendInt(dst, val, 10)
+}
+
+// AppendInts64 encodes the input int64s to json and
+// appends the encoded string list to the input byte slice.
+func (Encoder) AppendInts64(dst []byte, vals []int64) []byte {
+ if len(vals) == 0 {
+ return append(dst, '[', ']')
+ }
+ dst = append(dst, '[')
+ dst = strconv.AppendInt(dst, vals[0], 10)
+ if len(vals) > 1 {
+ for _, val := range vals[1:] {
+ dst = strconv.AppendInt(append(dst, ','), val, 10)
+ }
+ }
+ dst = append(dst, ']')
+ return dst
+}
+
+// AppendUint converts the input uint to a string and
+// appends the encoded string to the input byte slice.
+func (Encoder) AppendUint(dst []byte, val uint) []byte {
+ return strconv.AppendUint(dst, uint64(val), 10)
+}
+
+// AppendUints encodes the input uints to json and
+// appends the encoded string list to the input byte slice.
+func (Encoder) AppendUints(dst []byte, vals []uint) []byte {
+ if len(vals) == 0 {
+ return append(dst, '[', ']')
+ }
+ dst = append(dst, '[')
+ dst = strconv.AppendUint(dst, uint64(vals[0]), 10)
+ if len(vals) > 1 {
+ for _, val := range vals[1:] {
+ dst = strconv.AppendUint(append(dst, ','), uint64(val), 10)
+ }
+ }
+ dst = append(dst, ']')
+ return dst
+}
+
+// AppendUint8 converts the input uint8 to a string and
+// appends the encoded string to the input byte slice.
+func (Encoder) AppendUint8(dst []byte, val uint8) []byte {
+ return strconv.AppendUint(dst, uint64(val), 10)
+}
+
+// AppendUints8 encodes the input uint8s to json and
+// appends the encoded string list to the input byte slice.
+func (Encoder) AppendUints8(dst []byte, vals []uint8) []byte {
+ if len(vals) == 0 {
+ return append(dst, '[', ']')
+ }
+ dst = append(dst, '[')
+ dst = strconv.AppendUint(dst, uint64(vals[0]), 10)
+ if len(vals) > 1 {
+ for _, val := range vals[1:] {
+ dst = strconv.AppendUint(append(dst, ','), uint64(val), 10)
+ }
+ }
+ dst = append(dst, ']')
+ return dst
+}
+
+// AppendUint16 converts the input uint16 to a string and
+// appends the encoded string to the input byte slice.
+func (Encoder) AppendUint16(dst []byte, val uint16) []byte {
+ return strconv.AppendUint(dst, uint64(val), 10)
+}
+
+// AppendUints16 encodes the input uint16s to json and
+// appends the encoded string list to the input byte slice.
+func (Encoder) AppendUints16(dst []byte, vals []uint16) []byte {
+ if len(vals) == 0 {
+ return append(dst, '[', ']')
+ }
+ dst = append(dst, '[')
+ dst = strconv.AppendUint(dst, uint64(vals[0]), 10)
+ if len(vals) > 1 {
+ for _, val := range vals[1:] {
+ dst = strconv.AppendUint(append(dst, ','), uint64(val), 10)
+ }
+ }
+ dst = append(dst, ']')
+ return dst
+}
+
+// AppendUint32 converts the input uint32 to a string and
+// appends the encoded string to the input byte slice.
+func (Encoder) AppendUint32(dst []byte, val uint32) []byte {
+ return strconv.AppendUint(dst, uint64(val), 10)
+}
+
+// AppendUints32 encodes the input uint32s to json and
+// appends the encoded string list to the input byte slice.
+func (Encoder) AppendUints32(dst []byte, vals []uint32) []byte {
+ if len(vals) == 0 {
+ return append(dst, '[', ']')
+ }
+ dst = append(dst, '[')
+ dst = strconv.AppendUint(dst, uint64(vals[0]), 10)
+ if len(vals) > 1 {
+ for _, val := range vals[1:] {
+ dst = strconv.AppendUint(append(dst, ','), uint64(val), 10)
+ }
+ }
+ dst = append(dst, ']')
+ return dst
+}
+
+// AppendUint64 converts the input uint64 to a string and
+// appends the encoded string to the input byte slice.
+func (Encoder) AppendUint64(dst []byte, val uint64) []byte {
+ return strconv.AppendUint(dst, val, 10)
+}
+
+// AppendUints64 encodes the input uint64s to json and
+// appends the encoded string list to the input byte slice.
+func (Encoder) AppendUints64(dst []byte, vals []uint64) []byte {
+ if len(vals) == 0 {
+ return append(dst, '[', ']')
+ }
+ dst = append(dst, '[')
+ dst = strconv.AppendUint(dst, vals[0], 10)
+ if len(vals) > 1 {
+ for _, val := range vals[1:] {
+ dst = strconv.AppendUint(append(dst, ','), val, 10)
+ }
+ }
+ dst = append(dst, ']')
+ return dst
+}
+
+func appendFloat(dst []byte, val float64, bitSize, precision int) []byte {
+ // JSON does not permit NaN or Infinity. A typical JSON encoder would fail
+ // with an error, but a logging library wants the data to get through so we
+ // make a tradeoff and store those types as string.
+ switch {
+ case math.IsNaN(val):
+ return append(dst, `"NaN"`...)
+ case math.IsInf(val, 1):
+ return append(dst, `"+Inf"`...)
+ case math.IsInf(val, -1):
+ return append(dst, `"-Inf"`...)
+ }
+ // convert as if by es6 number to string conversion
+ // see also https://cs.opensource.google/go/go/+/refs/tags/go1.20.3:src/encoding/json/encode.go;l=573
+ strFmt := byte('f')
+ // If precision is set to a value other than -1, we always just format the float using that precision.
+ if precision == -1 {
+ // Use float32 comparisons for underlying float32 value to get precise cutoffs right.
+ if abs := math.Abs(val); abs != 0 {
+ if bitSize == 64 && (abs < 1e-6 || abs >= 1e21) || bitSize == 32 && (float32(abs) < 1e-6 || float32(abs) >= 1e21) {
+ strFmt = 'e'
+ }
+ }
+ }
+ dst = strconv.AppendFloat(dst, val, strFmt, precision, bitSize)
+ if strFmt == 'e' {
+ // Clean up e-09 to e-9
+ n := len(dst)
+ if n >= 4 && dst[n-4] == 'e' && dst[n-3] == '-' && dst[n-2] == '0' {
+ dst[n-2] = dst[n-1]
+ dst = dst[:n-1]
+ }
+ }
+ return dst
+}
+
+// AppendFloat32 converts the input float32 to a string and
+// appends the encoded string to the input byte slice.
+func (Encoder) AppendFloat32(dst []byte, val float32, precision int) []byte {
+ return appendFloat(dst, float64(val), 32, precision)
+}
+
+// AppendFloats32 encodes the input float32s to json and
+// appends the encoded string list to the input byte slice.
+func (Encoder) AppendFloats32(dst []byte, vals []float32, precision int) []byte {
+ if len(vals) == 0 {
+ return append(dst, '[', ']')
+ }
+ dst = append(dst, '[')
+ dst = appendFloat(dst, float64(vals[0]), 32, precision)
+ if len(vals) > 1 {
+ for _, val := range vals[1:] {
+ dst = appendFloat(append(dst, ','), float64(val), 32, precision)
+ }
+ }
+ dst = append(dst, ']')
+ return dst
+}
+
+// AppendFloat64 converts the input float64 to a string and
+// appends the encoded string to the input byte slice.
+func (Encoder) AppendFloat64(dst []byte, val float64, precision int) []byte {
+ return appendFloat(dst, val, 64, precision)
+}
+
+// AppendFloats64 encodes the input float64s to json and
+// appends the encoded string list to the input byte slice.
+func (Encoder) AppendFloats64(dst []byte, vals []float64, precision int) []byte {
+ if len(vals) == 0 {
+ return append(dst, '[', ']')
+ }
+ dst = append(dst, '[')
+ dst = appendFloat(dst, vals[0], 64, precision)
+ if len(vals) > 1 {
+ for _, val := range vals[1:] {
+ dst = appendFloat(append(dst, ','), val, 64, precision)
+ }
+ }
+ dst = append(dst, ']')
+ return dst
+}
+
+// AppendInterface marshals the input interface to a string and
+// appends the encoded string to the input byte slice.
+func (e Encoder) AppendInterface(dst []byte, i interface{}) []byte {
+ marshaled, err := JSONMarshalFunc(i)
+ if err != nil {
+ return e.AppendString(dst, fmt.Sprintf("marshaling error: %v", err))
+ }
+ return append(dst, marshaled...)
+}
+
+// AppendType appends the parameter type (as a string) to the input byte slice.
+func (e Encoder) AppendType(dst []byte, i interface{}) []byte {
+ if i == nil {
+ return e.AppendString(dst, "")
+ }
+ return e.AppendString(dst, reflect.TypeOf(i).String())
+}
+
+// AppendObjectData takes in an object that is already in a byte array
+// and adds it to the dst.
+func (Encoder) AppendObjectData(dst []byte, o []byte) []byte {
+ // Three conditions apply here:
+ // 1. new content starts with '{' - which should be dropped OR
+ // 2. new content starts with '{' - which should be replaced with ','
+ // to separate with existing content OR
+ // 3. existing content has already other fields
+ if o[0] == '{' {
+ if len(dst) > 1 {
+ dst = append(dst, ',')
+ }
+ o = o[1:]
+ } else if len(dst) > 1 {
+ dst = append(dst, ',')
+ }
+ return append(dst, o...)
+}
+
+// AppendIPAddr adds IPv4 or IPv6 address to dst.
+func (e Encoder) AppendIPAddr(dst []byte, ip net.IP) []byte {
+ return e.AppendString(dst, ip.String())
+}
+
+// AppendIPPrefix adds IPv4 or IPv6 Prefix (address & mask) to dst.
+func (e Encoder) AppendIPPrefix(dst []byte, pfx net.IPNet) []byte {
+ return e.AppendString(dst, pfx.String())
+
+}
+
+// AppendMACAddr adds MAC address to dst.
+func (e Encoder) AppendMACAddr(dst []byte, ha net.HardwareAddr) []byte {
+ return e.AppendString(dst, ha.String())
+}
diff --git a/vendor/github.com/rs/zerolog/log.go b/vendor/github.com/rs/zerolog/log.go
new file mode 100644
index 00000000..6c1d4ead
--- /dev/null
+++ b/vendor/github.com/rs/zerolog/log.go
@@ -0,0 +1,518 @@
+// Package zerolog provides a lightweight logging library dedicated to JSON logging.
+//
+// A global Logger can be use for simple logging:
+//
+// import "github.com/rs/zerolog/log"
+//
+// log.Info().Msg("hello world")
+// // Output: {"time":1494567715,"level":"info","message":"hello world"}
+//
+// NOTE: To import the global logger, import the "log" subpackage "github.com/rs/zerolog/log".
+//
+// Fields can be added to log messages:
+//
+// log.Info().Str("foo", "bar").Msg("hello world")
+// // Output: {"time":1494567715,"level":"info","message":"hello world","foo":"bar"}
+//
+// Create logger instance to manage different outputs:
+//
+// logger := zerolog.New(os.Stderr).With().Timestamp().Logger()
+// logger.Info().
+// Str("foo", "bar").
+// Msg("hello world")
+// // Output: {"time":1494567715,"level":"info","message":"hello world","foo":"bar"}
+//
+// Sub-loggers let you chain loggers with additional context:
+//
+// sublogger := log.With().Str("component", "foo").Logger()
+// sublogger.Info().Msg("hello world")
+// // Output: {"time":1494567715,"level":"info","message":"hello world","component":"foo"}
+//
+// Level logging
+//
+// zerolog.SetGlobalLevel(zerolog.InfoLevel)
+//
+// log.Debug().Msg("filtered out message")
+// log.Info().Msg("routed message")
+//
+// if e := log.Debug(); e.Enabled() {
+// // Compute log output only if enabled.
+// value := compute()
+// e.Str("foo": value).Msg("some debug message")
+// }
+// // Output: {"level":"info","time":1494567715,"routed message"}
+//
+// Customize automatic field names:
+//
+// log.TimestampFieldName = "t"
+// log.LevelFieldName = "p"
+// log.MessageFieldName = "m"
+//
+// log.Info().Msg("hello world")
+// // Output: {"t":1494567715,"p":"info","m":"hello world"}
+//
+// Log with no level and message:
+//
+// log.Log().Str("foo","bar").Msg("")
+// // Output: {"time":1494567715,"foo":"bar"}
+//
+// Add contextual fields to global Logger:
+//
+// log.Logger = log.With().Str("foo", "bar").Logger()
+//
+// Sample logs:
+//
+// sampled := log.Sample(&zerolog.BasicSampler{N: 10})
+// sampled.Info().Msg("will be logged every 10 messages")
+//
+// Log with contextual hooks:
+//
+// // Create the hook:
+// type SeverityHook struct{}
+//
+// func (h SeverityHook) Run(e *zerolog.Event, level zerolog.Level, msg string) {
+// if level != zerolog.NoLevel {
+// e.Str("severity", level.String())
+// }
+// }
+//
+// // And use it:
+// var h SeverityHook
+// log := zerolog.New(os.Stdout).Hook(h)
+// log.Warn().Msg("")
+// // Output: {"level":"warn","severity":"warn"}
+//
+// # Caveats
+//
+// Field duplication:
+//
+// There is no fields deduplication out-of-the-box.
+// Using the same key multiple times creates new key in final JSON each time.
+//
+// logger := zerolog.New(os.Stderr).With().Timestamp().Logger()
+// logger.Info().
+// Timestamp().
+// Msg("dup")
+// // Output: {"level":"info","time":1494567715,"time":1494567715,"message":"dup"}
+//
+// In this case, many consumers will take the last value,
+// but this is not guaranteed; check yours if in doubt.
+//
+// Concurrency safety:
+//
+// Be careful when calling UpdateContext. It is not concurrency safe. Use the With method to create a child logger:
+//
+// func handler(w http.ResponseWriter, r *http.Request) {
+// // Create a child logger for concurrency safety
+// logger := log.Logger.With().Logger()
+//
+// // Add context fields, for example User-Agent from HTTP headers
+// logger.UpdateContext(func(c zerolog.Context) zerolog.Context {
+// ...
+// })
+// }
+package zerolog
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "io"
+ "os"
+ "strconv"
+ "strings"
+)
+
+// Level defines log levels.
+type Level int8
+
+const (
+ // DebugLevel defines debug log level.
+ DebugLevel Level = iota
+ // InfoLevel defines info log level.
+ InfoLevel
+ // WarnLevel defines warn log level.
+ WarnLevel
+ // ErrorLevel defines error log level.
+ ErrorLevel
+ // FatalLevel defines fatal log level.
+ FatalLevel
+ // PanicLevel defines panic log level.
+ PanicLevel
+ // NoLevel defines an absent log level.
+ NoLevel
+ // Disabled disables the logger.
+ Disabled
+
+ // TraceLevel defines trace log level.
+ TraceLevel Level = -1
+ // Values less than TraceLevel are handled as numbers.
+)
+
+func (l Level) String() string {
+ switch l {
+ case TraceLevel:
+ return LevelTraceValue
+ case DebugLevel:
+ return LevelDebugValue
+ case InfoLevel:
+ return LevelInfoValue
+ case WarnLevel:
+ return LevelWarnValue
+ case ErrorLevel:
+ return LevelErrorValue
+ case FatalLevel:
+ return LevelFatalValue
+ case PanicLevel:
+ return LevelPanicValue
+ case Disabled:
+ return "disabled"
+ case NoLevel:
+ return ""
+ }
+ return strconv.Itoa(int(l))
+}
+
+// ParseLevel converts a level string into a zerolog Level value.
+// returns an error if the input string does not match known values.
+func ParseLevel(levelStr string) (Level, error) {
+ switch {
+ case strings.EqualFold(levelStr, LevelFieldMarshalFunc(TraceLevel)):
+ return TraceLevel, nil
+ case strings.EqualFold(levelStr, LevelFieldMarshalFunc(DebugLevel)):
+ return DebugLevel, nil
+ case strings.EqualFold(levelStr, LevelFieldMarshalFunc(InfoLevel)):
+ return InfoLevel, nil
+ case strings.EqualFold(levelStr, LevelFieldMarshalFunc(WarnLevel)):
+ return WarnLevel, nil
+ case strings.EqualFold(levelStr, LevelFieldMarshalFunc(ErrorLevel)):
+ return ErrorLevel, nil
+ case strings.EqualFold(levelStr, LevelFieldMarshalFunc(FatalLevel)):
+ return FatalLevel, nil
+ case strings.EqualFold(levelStr, LevelFieldMarshalFunc(PanicLevel)):
+ return PanicLevel, nil
+ case strings.EqualFold(levelStr, LevelFieldMarshalFunc(Disabled)):
+ return Disabled, nil
+ case strings.EqualFold(levelStr, LevelFieldMarshalFunc(NoLevel)):
+ return NoLevel, nil
+ }
+ i, err := strconv.Atoi(levelStr)
+ if err != nil {
+ return NoLevel, fmt.Errorf("Unknown Level String: '%s', defaulting to NoLevel", levelStr)
+ }
+ if i > 127 || i < -128 {
+ return NoLevel, fmt.Errorf("Out-Of-Bounds Level: '%d', defaulting to NoLevel", i)
+ }
+ return Level(i), nil
+}
+
+// UnmarshalText implements encoding.TextUnmarshaler to allow for easy reading from toml/yaml/json formats
+func (l *Level) UnmarshalText(text []byte) error {
+ if l == nil {
+ return errors.New("can't unmarshal a nil *Level")
+ }
+ var err error
+ *l, err = ParseLevel(string(text))
+ return err
+}
+
+// MarshalText implements encoding.TextMarshaler to allow for easy writing into toml/yaml/json formats
+func (l Level) MarshalText() ([]byte, error) {
+ return []byte(LevelFieldMarshalFunc(l)), nil
+}
+
+// A Logger represents an active logging object that generates lines
+// of JSON output to an io.Writer. Each logging operation makes a single
+// call to the Writer's Write method. There is no guarantee on access
+// serialization to the Writer. If your Writer is not thread safe,
+// you may consider a sync wrapper.
+type Logger struct {
+ w LevelWriter
+ level Level
+ sampler Sampler
+ context []byte
+ hooks []Hook
+ stack bool
+ ctx context.Context
+}
+
+// New creates a root logger with given output writer. If the output writer implements
+// the LevelWriter interface, the WriteLevel method will be called instead of the Write
+// one.
+//
+// Each logging operation makes a single call to the Writer's Write method. There is no
+// guarantee on access serialization to the Writer. If your Writer is not thread safe,
+// you may consider using sync wrapper.
+func New(w io.Writer) Logger {
+ if w == nil {
+ w = io.Discard
+ }
+ lw, ok := w.(LevelWriter)
+ if !ok {
+ lw = LevelWriterAdapter{w}
+ }
+ return Logger{w: lw, level: TraceLevel}
+}
+
+// Nop returns a disabled logger for which all operation are no-op.
+func Nop() Logger {
+ return New(nil).Level(Disabled)
+}
+
+// Output duplicates the current logger and sets w as its output.
+func (l Logger) Output(w io.Writer) Logger {
+ l2 := New(w)
+ l2.level = l.level
+ l2.sampler = l.sampler
+ l2.stack = l.stack
+ if len(l.hooks) > 0 {
+ l2.hooks = append(l2.hooks, l.hooks...)
+ }
+ if l.context != nil {
+ l2.context = make([]byte, len(l.context), cap(l.context))
+ copy(l2.context, l.context)
+ }
+ return l2
+}
+
+// With creates a child logger with the field added to its context.
+func (l Logger) With() Context {
+ context := l.context
+ l.context = make([]byte, 0, 500)
+ if context != nil {
+ l.context = append(l.context, context...)
+ } else {
+ // This is needed for AppendKey to not check len of input
+ // thus making it inlinable
+ l.context = enc.AppendBeginMarker(l.context)
+ }
+ return Context{l}
+}
+
+// UpdateContext updates the internal logger's context.
+//
+// Caution: This method is not concurrency safe.
+// Use the With method to create a child logger before modifying the context from concurrent goroutines.
+func (l *Logger) UpdateContext(update func(c Context) Context) {
+ if l == disabledLogger {
+ return
+ }
+ if cap(l.context) == 0 {
+ l.context = make([]byte, 0, 500)
+ }
+ if len(l.context) == 0 {
+ l.context = enc.AppendBeginMarker(l.context)
+ }
+ c := update(Context{*l})
+ l.context = c.l.context
+}
+
+// Level creates a child logger with the minimum accepted level set to level.
+func (l Logger) Level(lvl Level) Logger {
+ l.level = lvl
+ return l
+}
+
+// GetLevel returns the current Level of l.
+func (l Logger) GetLevel() Level {
+ return l.level
+}
+
+// Sample returns a logger with the s sampler.
+func (l Logger) Sample(s Sampler) Logger {
+ l.sampler = s
+ return l
+}
+
+// Hook returns a logger with the h Hook.
+func (l Logger) Hook(hooks ...Hook) Logger {
+ if len(hooks) == 0 {
+ return l
+ }
+ newHooks := make([]Hook, len(l.hooks), len(l.hooks)+len(hooks))
+ copy(newHooks, l.hooks)
+ l.hooks = append(newHooks, hooks...)
+ return l
+}
+
+// Trace starts a new message with trace level.
+//
+// You must call Msg on the returned event in order to send the event.
+func (l *Logger) Trace() *Event {
+ return l.newEvent(TraceLevel, nil)
+}
+
+// Debug starts a new message with debug level.
+//
+// You must call Msg on the returned event in order to send the event.
+func (l *Logger) Debug() *Event {
+ return l.newEvent(DebugLevel, nil)
+}
+
+// Info starts a new message with info level.
+//
+// You must call Msg on the returned event in order to send the event.
+func (l *Logger) Info() *Event {
+ return l.newEvent(InfoLevel, nil)
+}
+
+// Warn starts a new message with warn level.
+//
+// You must call Msg on the returned event in order to send the event.
+func (l *Logger) Warn() *Event {
+ return l.newEvent(WarnLevel, nil)
+}
+
+// Error starts a new message with error level.
+//
+// You must call Msg on the returned event in order to send the event.
+func (l *Logger) Error() *Event {
+ return l.newEvent(ErrorLevel, nil)
+}
+
+// Err starts a new message with error level with err as a field if not nil or
+// with info level if err is nil.
+//
+// You must call Msg on the returned event in order to send the event.
+func (l *Logger) Err(err error) *Event {
+ if err != nil {
+ return l.Error().Err(err)
+ }
+
+ return l.Info()
+}
+
+// Fatal starts a new message with fatal level. The os.Exit(1) function
+// is called by the Msg method, which terminates the program immediately.
+//
+// You must call Msg on the returned event in order to send the event.
+func (l *Logger) Fatal() *Event {
+ return l.newEvent(FatalLevel, func(msg string) {
+ if closer, ok := l.w.(io.Closer); ok {
+ // Close the writer to flush any buffered message. Otherwise the message
+ // will be lost as os.Exit() terminates the program immediately.
+ closer.Close()
+ }
+ os.Exit(1)
+ })
+}
+
+// Panic starts a new message with panic level. The panic() function
+// is called by the Msg method, which stops the ordinary flow of a goroutine.
+//
+// You must call Msg on the returned event in order to send the event.
+func (l *Logger) Panic() *Event {
+ return l.newEvent(PanicLevel, func(msg string) { panic(msg) })
+}
+
+// WithLevel starts a new message with level. Unlike Fatal and Panic
+// methods, WithLevel does not terminate the program or stop the ordinary
+// flow of a goroutine when used with their respective levels.
+//
+// You must call Msg on the returned event in order to send the event.
+func (l *Logger) WithLevel(level Level) *Event {
+ switch level {
+ case TraceLevel:
+ return l.Trace()
+ case DebugLevel:
+ return l.Debug()
+ case InfoLevel:
+ return l.Info()
+ case WarnLevel:
+ return l.Warn()
+ case ErrorLevel:
+ return l.Error()
+ case FatalLevel:
+ return l.newEvent(FatalLevel, nil)
+ case PanicLevel:
+ return l.newEvent(PanicLevel, nil)
+ case NoLevel:
+ return l.Log()
+ case Disabled:
+ return nil
+ default:
+ return l.newEvent(level, nil)
+ }
+}
+
+// Log starts a new message with no level. Setting GlobalLevel to Disabled
+// will still disable events produced by this method.
+//
+// You must call Msg on the returned event in order to send the event.
+func (l *Logger) Log() *Event {
+ return l.newEvent(NoLevel, nil)
+}
+
+// Print sends a log event using debug level and no extra field.
+// Arguments are handled in the manner of fmt.Print.
+func (l *Logger) Print(v ...interface{}) {
+ if e := l.Debug(); e.Enabled() {
+ e.CallerSkipFrame(1).Msg(fmt.Sprint(v...))
+ }
+}
+
+// Printf sends a log event using debug level and no extra field.
+// Arguments are handled in the manner of fmt.Printf.
+func (l *Logger) Printf(format string, v ...interface{}) {
+ if e := l.Debug(); e.Enabled() {
+ e.CallerSkipFrame(1).Msg(fmt.Sprintf(format, v...))
+ }
+}
+
+// Println sends a log event using debug level and no extra field.
+// Arguments are handled in the manner of fmt.Println.
+func (l *Logger) Println(v ...interface{}) {
+ if e := l.Debug(); e.Enabled() {
+ e.CallerSkipFrame(1).Msg(fmt.Sprintln(v...))
+ }
+}
+
+// Write implements the io.Writer interface. This is useful to set as a writer
+// for the standard library log.
+func (l Logger) Write(p []byte) (n int, err error) {
+ n = len(p)
+ if n > 0 && p[n-1] == '\n' {
+ // Trim CR added by stdlog.
+ p = p[0 : n-1]
+ }
+ l.Log().CallerSkipFrame(1).Msg(string(p))
+ return
+}
+
+func (l *Logger) newEvent(level Level, done func(string)) *Event {
+ enabled := l.should(level)
+ if !enabled {
+ if done != nil {
+ done("")
+ }
+ return nil
+ }
+ e := newEvent(l.w, level)
+ e.done = done
+ e.ch = l.hooks
+ e.ctx = l.ctx
+ if level != NoLevel && LevelFieldName != "" {
+ e.Str(LevelFieldName, LevelFieldMarshalFunc(level))
+ }
+ if l.context != nil && len(l.context) > 1 {
+ e.buf = enc.AppendObjectData(e.buf, l.context)
+ }
+ if l.stack {
+ e.Stack()
+ }
+ return e
+}
+
+// should returns true if the log event should be logged.
+func (l *Logger) should(lvl Level) bool {
+ if l.w == nil {
+ return false
+ }
+ if lvl < l.level || lvl < GlobalLevel() {
+ return false
+ }
+ if l.sampler != nil && !samplingDisabled() {
+ return l.sampler.Sample(lvl)
+ }
+ return true
+}
diff --git a/vendor/github.com/rs/zerolog/log/log.go b/vendor/github.com/rs/zerolog/log/log.go
new file mode 100644
index 00000000..a96ec506
--- /dev/null
+++ b/vendor/github.com/rs/zerolog/log/log.go
@@ -0,0 +1,131 @@
+// Package log provides a global logger for zerolog.
+package log
+
+import (
+ "context"
+ "fmt"
+ "io"
+ "os"
+
+ "github.com/rs/zerolog"
+)
+
+// Logger is the global logger.
+var Logger = zerolog.New(os.Stderr).With().Timestamp().Logger()
+
+// Output duplicates the global logger and sets w as its output.
+func Output(w io.Writer) zerolog.Logger {
+ return Logger.Output(w)
+}
+
+// With creates a child logger with the field added to its context.
+func With() zerolog.Context {
+ return Logger.With()
+}
+
+// Level creates a child logger with the minimum accepted level set to level.
+func Level(level zerolog.Level) zerolog.Logger {
+ return Logger.Level(level)
+}
+
+// Sample returns a logger with the s sampler.
+func Sample(s zerolog.Sampler) zerolog.Logger {
+ return Logger.Sample(s)
+}
+
+// Hook returns a logger with the h Hook.
+func Hook(h zerolog.Hook) zerolog.Logger {
+ return Logger.Hook(h)
+}
+
+// Err starts a new message with error level with err as a field if not nil or
+// with info level if err is nil.
+//
+// You must call Msg on the returned event in order to send the event.
+func Err(err error) *zerolog.Event {
+ return Logger.Err(err)
+}
+
+// Trace starts a new message with trace level.
+//
+// You must call Msg on the returned event in order to send the event.
+func Trace() *zerolog.Event {
+ return Logger.Trace()
+}
+
+// Debug starts a new message with debug level.
+//
+// You must call Msg on the returned event in order to send the event.
+func Debug() *zerolog.Event {
+ return Logger.Debug()
+}
+
+// Info starts a new message with info level.
+//
+// You must call Msg on the returned event in order to send the event.
+func Info() *zerolog.Event {
+ return Logger.Info()
+}
+
+// Warn starts a new message with warn level.
+//
+// You must call Msg on the returned event in order to send the event.
+func Warn() *zerolog.Event {
+ return Logger.Warn()
+}
+
+// Error starts a new message with error level.
+//
+// You must call Msg on the returned event in order to send the event.
+func Error() *zerolog.Event {
+ return Logger.Error()
+}
+
+// Fatal starts a new message with fatal level. The os.Exit(1) function
+// is called by the Msg method.
+//
+// You must call Msg on the returned event in order to send the event.
+func Fatal() *zerolog.Event {
+ return Logger.Fatal()
+}
+
+// Panic starts a new message with panic level. The message is also sent
+// to the panic function.
+//
+// You must call Msg on the returned event in order to send the event.
+func Panic() *zerolog.Event {
+ return Logger.Panic()
+}
+
+// WithLevel starts a new message with level.
+//
+// You must call Msg on the returned event in order to send the event.
+func WithLevel(level zerolog.Level) *zerolog.Event {
+ return Logger.WithLevel(level)
+}
+
+// Log starts a new message with no level. Setting zerolog.GlobalLevel to
+// zerolog.Disabled will still disable events produced by this method.
+//
+// You must call Msg on the returned event in order to send the event.
+func Log() *zerolog.Event {
+ return Logger.Log()
+}
+
+// Print sends a log event using debug level and no extra field.
+// Arguments are handled in the manner of fmt.Print.
+func Print(v ...interface{}) {
+ Logger.Debug().CallerSkipFrame(1).Msg(fmt.Sprint(v...))
+}
+
+// Printf sends a log event using debug level and no extra field.
+// Arguments are handled in the manner of fmt.Printf.
+func Printf(format string, v ...interface{}) {
+ Logger.Debug().CallerSkipFrame(1).Msgf(format, v...)
+}
+
+// Ctx returns the Logger associated with the ctx. If no logger
+// is associated, a disabled logger is returned.
+func Ctx(ctx context.Context) *zerolog.Logger {
+ return zerolog.Ctx(ctx)
+}
diff --git a/vendor/github.com/rs/zerolog/not_go112.go b/vendor/github.com/rs/zerolog/not_go112.go
new file mode 100644
index 00000000..4c43c9e7
--- /dev/null
+++ b/vendor/github.com/rs/zerolog/not_go112.go
@@ -0,0 +1,5 @@
+// +build !go1.12
+
+package zerolog
+
+const contextCallerSkipFrameCount = 3
diff --git a/vendor/github.com/rs/zerolog/pretty.png b/vendor/github.com/rs/zerolog/pretty.png
new file mode 100644
index 00000000..1449e45d
Binary files /dev/null and b/vendor/github.com/rs/zerolog/pretty.png differ
diff --git a/vendor/github.com/rs/zerolog/sampler.go b/vendor/github.com/rs/zerolog/sampler.go
new file mode 100644
index 00000000..83ce2ed3
--- /dev/null
+++ b/vendor/github.com/rs/zerolog/sampler.go
@@ -0,0 +1,134 @@
+package zerolog
+
+import (
+ "math/rand"
+ "sync/atomic"
+ "time"
+)
+
+var (
+ // Often samples log every ~ 10 events.
+ Often = RandomSampler(10)
+ // Sometimes samples log every ~ 100 events.
+ Sometimes = RandomSampler(100)
+ // Rarely samples log every ~ 1000 events.
+ Rarely = RandomSampler(1000)
+)
+
+// Sampler defines an interface to a log sampler.
+type Sampler interface {
+ // Sample returns true if the event should be part of the sample, false if
+ // the event should be dropped.
+ Sample(lvl Level) bool
+}
+
+// RandomSampler use a PRNG to randomly sample an event out of N events,
+// regardless of their level.
+type RandomSampler uint32
+
+// Sample implements the Sampler interface.
+func (s RandomSampler) Sample(lvl Level) bool {
+ if s <= 0 {
+ return false
+ }
+ if rand.Intn(int(s)) != 0 {
+ return false
+ }
+ return true
+}
+
+// BasicSampler is a sampler that will send every Nth events, regardless of
+// their level.
+type BasicSampler struct {
+ N uint32
+ counter uint32
+}
+
+// Sample implements the Sampler interface.
+func (s *BasicSampler) Sample(lvl Level) bool {
+ n := s.N
+ if n == 1 {
+ return true
+ }
+ c := atomic.AddUint32(&s.counter, 1)
+ return c%n == 1
+}
+
+// BurstSampler lets Burst events pass per Period then pass the decision to
+// NextSampler. If Sampler is not set, all subsequent events are rejected.
+type BurstSampler struct {
+ // Burst is the maximum number of event per period allowed before calling
+ // NextSampler.
+ Burst uint32
+ // Period defines the burst period. If 0, NextSampler is always called.
+ Period time.Duration
+ // NextSampler is the sampler used after the burst is reached. If nil,
+ // events are always rejected after the burst.
+ NextSampler Sampler
+
+ counter uint32
+ resetAt int64
+}
+
+// Sample implements the Sampler interface.
+func (s *BurstSampler) Sample(lvl Level) bool {
+ if s.Burst > 0 && s.Period > 0 {
+ if s.inc() <= s.Burst {
+ return true
+ }
+ }
+ if s.NextSampler == nil {
+ return false
+ }
+ return s.NextSampler.Sample(lvl)
+}
+
+func (s *BurstSampler) inc() uint32 {
+ now := TimestampFunc().UnixNano()
+ resetAt := atomic.LoadInt64(&s.resetAt)
+ var c uint32
+ if now > resetAt {
+ c = 1
+ atomic.StoreUint32(&s.counter, c)
+ newResetAt := now + s.Period.Nanoseconds()
+ reset := atomic.CompareAndSwapInt64(&s.resetAt, resetAt, newResetAt)
+ if !reset {
+ // Lost the race with another goroutine trying to reset.
+ c = atomic.AddUint32(&s.counter, 1)
+ }
+ } else {
+ c = atomic.AddUint32(&s.counter, 1)
+ }
+ return c
+}
+
+// LevelSampler applies a different sampler for each level.
+type LevelSampler struct {
+ TraceSampler, DebugSampler, InfoSampler, WarnSampler, ErrorSampler Sampler
+}
+
+func (s LevelSampler) Sample(lvl Level) bool {
+ switch lvl {
+ case TraceLevel:
+ if s.TraceSampler != nil {
+ return s.TraceSampler.Sample(lvl)
+ }
+ case DebugLevel:
+ if s.DebugSampler != nil {
+ return s.DebugSampler.Sample(lvl)
+ }
+ case InfoLevel:
+ if s.InfoSampler != nil {
+ return s.InfoSampler.Sample(lvl)
+ }
+ case WarnLevel:
+ if s.WarnSampler != nil {
+ return s.WarnSampler.Sample(lvl)
+ }
+ case ErrorLevel:
+ if s.ErrorSampler != nil {
+ return s.ErrorSampler.Sample(lvl)
+ }
+ }
+ return true
+}
diff --git a/vendor/github.com/rs/zerolog/syslog.go b/vendor/github.com/rs/zerolog/syslog.go
new file mode 100644
index 00000000..a2b7285d
--- /dev/null
+++ b/vendor/github.com/rs/zerolog/syslog.go
@@ -0,0 +1,89 @@
+// +build !windows
+// +build !binary_log
+
+package zerolog
+
+import (
+ "io"
+)
+
+// See http://cee.mitre.org/language/1.0-beta1/clt.html#syslog
+// or https://www.rsyslog.com/json-elasticsearch/
+const ceePrefix = "@cee:"
+
+// SyslogWriter is an interface matching a syslog.Writer struct.
+type SyslogWriter interface {
+ io.Writer
+ Debug(m string) error
+ Info(m string) error
+ Warning(m string) error
+ Err(m string) error
+ Emerg(m string) error
+ Crit(m string) error
+}
+
+type syslogWriter struct {
+ w SyslogWriter
+ prefix string
+}
+
+// SyslogLevelWriter wraps a SyslogWriter and call the right syslog level
+// method matching the zerolog level.
+func SyslogLevelWriter(w SyslogWriter) LevelWriter {
+ return syslogWriter{w, ""}
+}
+
+// SyslogCEEWriter wraps a SyslogWriter with a SyslogLevelWriter that adds a
+// MITRE CEE prefix for JSON syslog entries, compatible with rsyslog
+// and syslog-ng JSON logging support.
+// See https://www.rsyslog.com/json-elasticsearch/
+func SyslogCEEWriter(w SyslogWriter) LevelWriter {
+ return syslogWriter{w, ceePrefix}
+}
+
+func (sw syslogWriter) Write(p []byte) (n int, err error) {
+ var pn int
+ if sw.prefix != "" {
+ pn, err = sw.w.Write([]byte(sw.prefix))
+ if err != nil {
+ return pn, err
+ }
+ }
+ n, err = sw.w.Write(p)
+ return pn + n, err
+}
+
+// WriteLevel implements LevelWriter interface.
+func (sw syslogWriter) WriteLevel(level Level, p []byte) (n int, err error) {
+ switch level {
+ case TraceLevel:
+ case DebugLevel:
+ err = sw.w.Debug(sw.prefix + string(p))
+ case InfoLevel:
+ err = sw.w.Info(sw.prefix + string(p))
+ case WarnLevel:
+ err = sw.w.Warning(sw.prefix + string(p))
+ case ErrorLevel:
+ err = sw.w.Err(sw.prefix + string(p))
+ case FatalLevel:
+ err = sw.w.Emerg(sw.prefix + string(p))
+ case PanicLevel:
+ err = sw.w.Crit(sw.prefix + string(p))
+ case NoLevel:
+ err = sw.w.Info(sw.prefix + string(p))
+ default:
+ panic("invalid level")
+ }
+ // Any CEE prefix is not part of the message, so we don't include its length
+ n = len(p)
+ return
+}
+
+// Call the underlying writer's Close method if it is an io.Closer. Otherwise
+// does nothing.
+func (sw syslogWriter) Close() error {
+ if c, ok := sw.w.(io.Closer); ok {
+ return c.Close()
+ }
+ return nil
+}
diff --git a/vendor/github.com/rs/zerolog/writer.go b/vendor/github.com/rs/zerolog/writer.go
new file mode 100644
index 00000000..41b394d7
--- /dev/null
+++ b/vendor/github.com/rs/zerolog/writer.go
@@ -0,0 +1,346 @@
+package zerolog
+
+import (
+ "bytes"
+ "io"
+ "path"
+ "runtime"
+ "strconv"
+ "strings"
+ "sync"
+)
+
+// LevelWriter defines as interface a writer may implement in order
+// to receive level information with payload.
+type LevelWriter interface {
+ io.Writer
+ WriteLevel(level Level, p []byte) (n int, err error)
+}
+
+// LevelWriterAdapter adapts an io.Writer to support the LevelWriter interface.
+type LevelWriterAdapter struct {
+ io.Writer
+}
+
+// WriteLevel simply writes everything to the adapted writer, ignoring the level.
+func (lw LevelWriterAdapter) WriteLevel(l Level, p []byte) (n int, err error) {
+ return lw.Write(p)
+}
+
+// Call the underlying writer's Close method if it is an io.Closer. Otherwise
+// does nothing.
+func (lw LevelWriterAdapter) Close() error {
+ if closer, ok := lw.Writer.(io.Closer); ok {
+ return closer.Close()
+ }
+ return nil
+}
+
+type syncWriter struct {
+ mu sync.Mutex
+ lw LevelWriter
+}
+
+// SyncWriter wraps w so that each call to Write is synchronized with a mutex.
+// This syncer can be used to wrap the call to writer's Write method if it is
+// not thread safe. Note that you do not need this wrapper for os.File Write
+// operations on POSIX and Windows systems as they are already thread-safe.
+func SyncWriter(w io.Writer) io.Writer {
+ if lw, ok := w.(LevelWriter); ok {
+ return &syncWriter{lw: lw}
+ }
+ return &syncWriter{lw: LevelWriterAdapter{w}}
+}
+
+// Write implements the io.Writer interface.
+func (s *syncWriter) Write(p []byte) (n int, err error) {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ return s.lw.Write(p)
+}
+
+// WriteLevel implements the LevelWriter interface.
+func (s *syncWriter) WriteLevel(l Level, p []byte) (n int, err error) {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ return s.lw.WriteLevel(l, p)
+}
+
+func (s *syncWriter) Close() error {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ if closer, ok := s.lw.(io.Closer); ok {
+ return closer.Close()
+ }
+ return nil
+}
+
+type multiLevelWriter struct {
+ writers []LevelWriter
+}
+
+func (t multiLevelWriter) Write(p []byte) (n int, err error) {
+ for _, w := range t.writers {
+ if _n, _err := w.Write(p); err == nil {
+ n = _n
+ if _err != nil {
+ err = _err
+ } else if _n != len(p) {
+ err = io.ErrShortWrite
+ }
+ }
+ }
+ return n, err
+}
+
+func (t multiLevelWriter) WriteLevel(l Level, p []byte) (n int, err error) {
+ for _, w := range t.writers {
+ if _n, _err := w.WriteLevel(l, p); err == nil {
+ n = _n
+ if _err != nil {
+ err = _err
+ } else if _n != len(p) {
+ err = io.ErrShortWrite
+ }
+ }
+ }
+ return n, err
+}
+
+// Calls close on all the underlying writers that are io.Closers. If any of the
+// Close methods return an error, the remainder of the closers are not closed
+// and the error is returned.
+func (t multiLevelWriter) Close() error {
+ for _, w := range t.writers {
+ if closer, ok := w.(io.Closer); ok {
+ if err := closer.Close(); err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
+
+// MultiLevelWriter creates a writer that duplicates its writes to all the
+// provided writers, similar to the Unix tee(1) command. If some writers
+// implement LevelWriter, their WriteLevel method will be used instead of Write.
+func MultiLevelWriter(writers ...io.Writer) LevelWriter {
+ lwriters := make([]LevelWriter, 0, len(writers))
+ for _, w := range writers {
+ if lw, ok := w.(LevelWriter); ok {
+ lwriters = append(lwriters, lw)
+ } else {
+ lwriters = append(lwriters, LevelWriterAdapter{w})
+ }
+ }
+ return multiLevelWriter{lwriters}
+}
+
+// TestingLog is the logging interface of testing.TB.
+type TestingLog interface {
+ Log(args ...interface{})
+ Logf(format string, args ...interface{})
+ Helper()
+}
+
+// TestWriter is a writer that writes to testing.TB.
+type TestWriter struct {
+ T TestingLog
+
+ // Frame skips caller frames to capture the original file and line numbers.
+ Frame int
+}
+
+// NewTestWriter creates a writer that logs to the testing.TB.
+func NewTestWriter(t TestingLog) TestWriter {
+ return TestWriter{T: t}
+}
+
+// Write to testing.TB.
+func (t TestWriter) Write(p []byte) (n int, err error) {
+ t.T.Helper()
+
+ n = len(p)
+
+ // Strip trailing newline because t.Log always adds one.
+ p = bytes.TrimRight(p, "\n")
+
+ // Try to correct the log file and line number to the caller.
+ if t.Frame > 0 {
+ _, origFile, origLine, _ := runtime.Caller(1)
+ _, frameFile, frameLine, ok := runtime.Caller(1 + t.Frame)
+ if ok {
+ erase := strings.Repeat("\b", len(path.Base(origFile))+len(strconv.Itoa(origLine))+3)
+ t.T.Logf("%s%s:%d: %s", erase, path.Base(frameFile), frameLine, p)
+ return n, err
+ }
+ }
+ t.T.Log(string(p))
+
+ return n, err
+}
+
+// ConsoleTestWriter creates an option that correctly sets the file frame depth for testing.TB log.
+func ConsoleTestWriter(t TestingLog) func(w *ConsoleWriter) {
+ return func(w *ConsoleWriter) {
+ w.Out = TestWriter{T: t, Frame: 6}
+ }
+}
+
+// FilteredLevelWriter writes only logs at Level or above to Writer.
+//
+// It should be used only in combination with MultiLevelWriter when you
+// want to write to multiple destinations at different levels. Otherwise
+// you should just set the level on the logger and filter events early.
+// When using MultiLevelWriter then you set the level on the logger to
+// the lowest of the levels you use for writers.
+type FilteredLevelWriter struct {
+ Writer LevelWriter
+ Level Level
+}
+
+// Write writes to the underlying Writer.
+func (w *FilteredLevelWriter) Write(p []byte) (int, error) {
+ return w.Writer.Write(p)
+}
+
+// WriteLevel calls WriteLevel of the underlying Writer only if the level is equal
+// or above the Level.
+func (w *FilteredLevelWriter) WriteLevel(level Level, p []byte) (int, error) {
+ if level >= w.Level {
+ return w.Writer.WriteLevel(level, p)
+ }
+ return len(p), nil
+}
+
+var triggerWriterPool = &sync.Pool{
+ New: func() interface{} {
+ return bytes.NewBuffer(make([]byte, 0, 1024))
+ },
+}
+
+// TriggerLevelWriter buffers log lines at the ConditionalLevel or below
+// until a trigger level (or higher) line is emitted. Log lines with level
+// higher than ConditionalLevel are always written out to the destination
+// writer. If trigger never happens, buffered log lines are never written out.
+//
+// It can be used to configure "log level per request".
+type TriggerLevelWriter struct {
+ // Destination writer. If LevelWriter is provided (usually), its WriteLevel is used
+ // instead of Write.
+ io.Writer
+
+ // ConditionalLevel is the level (and below) at which lines are buffered until
+ // a trigger level (or higher) line is emitted. Usually this is set to DebugLevel.
+ ConditionalLevel Level
+
+ // TriggerLevel is the lowest level that triggers the sending of the conditional
+ // level lines. Usually this is set to ErrorLevel.
+ TriggerLevel Level
+
+ buf *bytes.Buffer
+ triggered bool
+ mu sync.Mutex
+}
+
+func (w *TriggerLevelWriter) WriteLevel(l Level, p []byte) (n int, err error) {
+ w.mu.Lock()
+ defer w.mu.Unlock()
+
+ // At first trigger level or above log line, we flush the buffer and change the
+ // trigger state to triggered.
+ if !w.triggered && l >= w.TriggerLevel {
+ err := w.trigger()
+ if err != nil {
+ return 0, err
+ }
+ }
+
+ // Unless triggered, we buffer everything at and below ConditionalLevel.
+ if !w.triggered && l <= w.ConditionalLevel {
+ if w.buf == nil {
+ w.buf = triggerWriterPool.Get().(*bytes.Buffer)
+ }
+
+ // We prefix each log line with a byte with the level.
+ // Hopefully we will never have a level value which equals a newline
+ // (which could interfere with reconstruction of log lines in the trigger method).
+ w.buf.WriteByte(byte(l))
+ w.buf.Write(p)
+ return len(p), nil
+ }
+
+ // Anything above ConditionalLevel is always passed through.
+ // Once triggered, everything is passed through.
+ if lw, ok := w.Writer.(LevelWriter); ok {
+ return lw.WriteLevel(l, p)
+ }
+ return w.Write(p)
+}
+
+// trigger expects lock to be held.
+func (w *TriggerLevelWriter) trigger() error {
+ if w.triggered {
+ return nil
+ }
+ w.triggered = true
+
+ if w.buf == nil {
+ return nil
+ }
+
+ p := w.buf.Bytes()
+ for len(p) > 0 {
+ // We do not use bufio.Scanner here because we already have full buffer
+ // in the memory and we do not want extra copying from the buffer to
+ // scanner's token slice, nor we want to hit scanner's token size limit,
+ // and we also want to preserve newlines.
+ i := bytes.IndexByte(p, '\n')
+ line := p[0 : i+1]
+ p = p[i+1:]
+ // We prefixed each log line with a byte with the level.
+ level := Level(line[0])
+ line = line[1:]
+ var err error
+ if lw, ok := w.Writer.(LevelWriter); ok {
+ _, err = lw.WriteLevel(level, line)
+ } else {
+ _, err = w.Write(line)
+ }
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+// Trigger forces flushing the buffer and change the trigger state to
+// triggered, if the writer has not already been triggered before.
+func (w *TriggerLevelWriter) Trigger() error {
+ w.mu.Lock()
+ defer w.mu.Unlock()
+
+ return w.trigger()
+}
+
+// Close closes the writer and returns the buffer to the pool.
+func (w *TriggerLevelWriter) Close() error {
+ w.mu.Lock()
+ defer w.mu.Unlock()
+
+ if w.buf == nil {
+ return nil
+ }
+
+ // We return the buffer only if it has not grown above the limit.
+ // This prevents accumulation of large buffers in the pool just
+ // because occasionally a large buffer might be needed.
+ if w.buf.Cap() <= TriggerLevelWriterBufferReuseLimit {
+ w.buf.Reset()
+ triggerWriterPool.Put(w.buf)
+ }
+ w.buf = nil
+
+ return nil
+}
diff --git a/vendor/github.com/sagikazarmark/locafero/.editorconfig b/vendor/github.com/sagikazarmark/locafero/.editorconfig
new file mode 100644
index 00000000..6f944f54
--- /dev/null
+++ b/vendor/github.com/sagikazarmark/locafero/.editorconfig
@@ -0,0 +1,21 @@
+root = true
+
+[*]
+charset = utf-8
+end_of_line = lf
+indent_size = 4
+indent_style = space
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[{Makefile,*.mk}]
+indent_style = tab
+
+[*.nix]
+indent_size = 2
+
+[*.go]
+indent_style = tab
+
+[{*.yml,*.yaml}]
+indent_size = 2
diff --git a/vendor/github.com/sagikazarmark/locafero/.envrc b/vendor/github.com/sagikazarmark/locafero/.envrc
new file mode 100644
index 00000000..3ce7171a
--- /dev/null
+++ b/vendor/github.com/sagikazarmark/locafero/.envrc
@@ -0,0 +1,4 @@
+if ! has nix_direnv_version || ! nix_direnv_version 2.3.0; then
+ source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/2.3.0/direnvrc" "sha256-Dmd+j63L84wuzgyjITIfSxSD57Tx7v51DMxVZOsiUD8="
+fi
+use flake . --impure
diff --git a/vendor/github.com/sagikazarmark/locafero/.gitignore b/vendor/github.com/sagikazarmark/locafero/.gitignore
new file mode 100644
index 00000000..8f07e601
--- /dev/null
+++ b/vendor/github.com/sagikazarmark/locafero/.gitignore
@@ -0,0 +1,8 @@
+/.devenv/
+/.direnv/
+/.task/
+/bin/
+/build/
+/tmp/
+/var/
+/vendor/
diff --git a/vendor/github.com/sagikazarmark/locafero/.golangci.yaml b/vendor/github.com/sagikazarmark/locafero/.golangci.yaml
new file mode 100644
index 00000000..829de2a4
--- /dev/null
+++ b/vendor/github.com/sagikazarmark/locafero/.golangci.yaml
@@ -0,0 +1,27 @@
+run:
+ timeout: 10m
+
+linters-settings:
+ gci:
+ sections:
+ - standard
+ - default
+ - prefix(github.com/sagikazarmark/locafero)
+ goimports:
+ local-prefixes: github.com/sagikazarmark/locafero
+ misspell:
+ locale: US
+ nolintlint:
+ allow-leading-space: false # require machine-readable nolint directives (with no leading space)
+ allow-unused: false # report any unused nolint directives
+ require-specific: false # don't require nolint directives to be specific about which linter is being skipped
+ revive:
+ confidence: 0
+
+linters:
+ enable:
+ - gci
+ - goimports
+ - misspell
+ - nolintlint
+ - revive
diff --git a/vendor/github.com/sagikazarmark/locafero/LICENSE b/vendor/github.com/sagikazarmark/locafero/LICENSE
new file mode 100644
index 00000000..a70b0f29
--- /dev/null
+++ b/vendor/github.com/sagikazarmark/locafero/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2023 Márk Sági-Kazár
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/github.com/sagikazarmark/locafero/README.md b/vendor/github.com/sagikazarmark/locafero/README.md
new file mode 100644
index 00000000..a48e8e97
--- /dev/null
+++ b/vendor/github.com/sagikazarmark/locafero/README.md
@@ -0,0 +1,37 @@
+# Finder library for [Afero](https://github.com/spf13/afero)
+
+[](https://github.com/sagikazarmark/locafero/actions/workflows/ci.yaml)
+[](https://pkg.go.dev/mod/github.com/sagikazarmark/locafero)
+
+[](https://builtwithnix.org)
+
+**Finder library for [Afero](https://github.com/spf13/afero) ported from [go-finder](https://github.com/sagikazarmark/go-finder).**
+
+> [!WARNING]
+> This is an experimental library under development.
+>
+> **Backwards compatibility is not guaranteed, expect breaking changes.**
+
+## Installation
+
+```shell
+go get github.com/sagikazarmark/locafero
+```
+
+## Usage
+
+Check out the [package example](https://pkg.go.dev/github.com/sagikazarmark/locafero#example-package) on go.dev.
+
+## Development
+
+**For an optimal developer experience, it is recommended to install [Nix](https://nixos.org/download.html) and [direnv](https://direnv.net/docs/installation.html).**
+
+Run the test suite:
+
+```shell
+just test
+```
+
+## License
+
+The project is licensed under the [MIT License](LICENSE).
diff --git a/vendor/github.com/sagikazarmark/locafero/file_type.go b/vendor/github.com/sagikazarmark/locafero/file_type.go
new file mode 100644
index 00000000..9a9b1402
--- /dev/null
+++ b/vendor/github.com/sagikazarmark/locafero/file_type.go
@@ -0,0 +1,28 @@
+package locafero
+
+import "io/fs"
+
+// FileType represents the kind of entries [Finder] can return.
+type FileType int
+
+const (
+ FileTypeAll FileType = iota
+ FileTypeFile
+ FileTypeDir
+)
+
+func (ft FileType) matchFileInfo(info fs.FileInfo) bool {
+ switch ft {
+ case FileTypeAll:
+ return true
+
+ case FileTypeFile:
+ return !info.IsDir()
+
+ case FileTypeDir:
+ return info.IsDir()
+
+ default:
+ return false
+ }
+}
diff --git a/vendor/github.com/sagikazarmark/locafero/finder.go b/vendor/github.com/sagikazarmark/locafero/finder.go
new file mode 100644
index 00000000..754c8b26
--- /dev/null
+++ b/vendor/github.com/sagikazarmark/locafero/finder.go
@@ -0,0 +1,165 @@
+// Package finder looks for files and directories in an {fs.Fs} filesystem.
+package locafero
+
+import (
+ "errors"
+ "io/fs"
+ "path/filepath"
+ "strings"
+
+ "github.com/sourcegraph/conc/iter"
+ "github.com/spf13/afero"
+)
+
+// Finder looks for files and directories in an [afero.Fs] filesystem.
+type Finder struct {
+ // Paths represents a list of locations that the [Finder] will search in.
+ //
+ // They are essentially the root directories or starting points for the search.
+ //
+ // Examples:
+ // - home/user
+ // - etc
+ Paths []string
+
+ // Names are specific entries that the [Finder] will look for within the given Paths.
+ //
+ // It provides the capability to search for entries with depth,
+ // meaning it can target deeper locations within the directory structure.
+ //
+ // It also supports glob syntax (as defined by [filepat.Match]), offering greater flexibility in search patterns.
+ //
+ // Examples:
+ // - config.yaml
+ // - home/*/config.yaml
+ // - home/*/config.*
+ Names []string
+
+ // Type restricts the kind of entries returned by the [Finder].
+ //
+ // This parameter helps in differentiating and filtering out files from directories or vice versa.
+ Type FileType
+}
+
+// Find looks for files and directories in an [afero.Fs] filesystem.
+func (f Finder) Find(fsys afero.Fs) ([]string, error) {
+ // Arbitrary go routine limit (TODO: make this a parameter)
+ // pool := pool.NewWithResults[[]string]().WithMaxGoroutines(5).WithErrors().WithFirstError()
+
+ type searchItem struct {
+ path string
+ name string
+ }
+
+ var searchItems []searchItem
+
+ for _, searchPath := range f.Paths {
+ searchPath := searchPath
+
+ for _, searchName := range f.Names {
+ searchName := searchName
+
+ searchItems = append(searchItems, searchItem{searchPath, searchName})
+
+ // pool.Go(func() ([]string, error) {
+ // // If the name contains any glob character, perform a glob match
+ // if strings.ContainsAny(searchName, "*?[]\\^") {
+ // return globWalkSearch(fsys, searchPath, searchName, f.Type)
+ // }
+ //
+ // return statSearch(fsys, searchPath, searchName, f.Type)
+ // })
+ }
+ }
+
+ // allResults, err := pool.Wait()
+ // if err != nil {
+ // return nil, err
+ // }
+
+ allResults, err := iter.MapErr(searchItems, func(item *searchItem) ([]string, error) {
+ // If the name contains any glob character, perform a glob match
+ if strings.ContainsAny(item.name, "*?[]\\^") {
+ return globWalkSearch(fsys, item.path, item.name, f.Type)
+ }
+
+ return statSearch(fsys, item.path, item.name, f.Type)
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ var results []string
+
+ for _, r := range allResults {
+ results = append(results, r...)
+ }
+
+ // Sort results in alphabetical order for now
+ // sort.Strings(results)
+
+ return results, nil
+}
+
+func globWalkSearch(fsys afero.Fs, searchPath string, searchName string, searchType FileType) ([]string, error) {
+ var results []string
+
+ err := afero.Walk(fsys, searchPath, func(p string, fileInfo fs.FileInfo, err error) error {
+ if err != nil {
+ return err
+ }
+
+ // Skip the root path
+ if p == searchPath {
+ return nil
+ }
+
+ var result error
+
+ // Stop reading subdirectories
+ // TODO: add depth detection here
+ if fileInfo.IsDir() && filepath.Dir(p) == searchPath {
+ result = fs.SkipDir
+ }
+
+ // Skip unmatching type
+ if !searchType.matchFileInfo(fileInfo) {
+ return result
+ }
+
+ match, err := filepath.Match(searchName, fileInfo.Name())
+ if err != nil {
+ return err
+ }
+
+ if match {
+ results = append(results, p)
+ }
+
+ return result
+ })
+ if err != nil {
+ return results, err
+ }
+
+ return results, nil
+}
+
+func statSearch(fsys afero.Fs, searchPath string, searchName string, searchType FileType) ([]string, error) {
+ filePath := filepath.Join(searchPath, searchName)
+
+ fileInfo, err := fsys.Stat(filePath)
+ if errors.Is(err, fs.ErrNotExist) {
+ return nil, nil
+ }
+ if err != nil {
+ return nil, err
+ }
+
+ // Skip unmatching type
+ if !searchType.matchFileInfo(fileInfo) {
+ return nil, nil
+ }
+
+ return []string{filePath}, nil
+}
diff --git a/vendor/github.com/sagikazarmark/locafero/flake.lock b/vendor/github.com/sagikazarmark/locafero/flake.lock
new file mode 100644
index 00000000..46d28f80
--- /dev/null
+++ b/vendor/github.com/sagikazarmark/locafero/flake.lock
@@ -0,0 +1,273 @@
+{
+ "nodes": {
+ "devenv": {
+ "inputs": {
+ "flake-compat": "flake-compat",
+ "nix": "nix",
+ "nixpkgs": "nixpkgs",
+ "pre-commit-hooks": "pre-commit-hooks"
+ },
+ "locked": {
+ "lastModified": 1694097209,
+ "narHash": "sha256-gQmBjjxeSyySjbh0yQVBKApo2KWIFqqbRUvG+Fa+QpM=",
+ "owner": "cachix",
+ "repo": "devenv",
+ "rev": "7a8e6a91510efe89d8dcb8e43233f93e86f6b189",
+ "type": "github"
+ },
+ "original": {
+ "owner": "cachix",
+ "repo": "devenv",
+ "type": "github"
+ }
+ },
+ "flake-compat": {
+ "flake": false,
+ "locked": {
+ "lastModified": 1673956053,
+ "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
+ "owner": "edolstra",
+ "repo": "flake-compat",
+ "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
+ "type": "github"
+ },
+ "original": {
+ "owner": "edolstra",
+ "repo": "flake-compat",
+ "type": "github"
+ }
+ },
+ "flake-parts": {
+ "inputs": {
+ "nixpkgs-lib": "nixpkgs-lib"
+ },
+ "locked": {
+ "lastModified": 1693611461,
+ "narHash": "sha256-aPODl8vAgGQ0ZYFIRisxYG5MOGSkIczvu2Cd8Gb9+1Y=",
+ "owner": "hercules-ci",
+ "repo": "flake-parts",
+ "rev": "7f53fdb7bdc5bb237da7fefef12d099e4fd611ca",
+ "type": "github"
+ },
+ "original": {
+ "owner": "hercules-ci",
+ "repo": "flake-parts",
+ "type": "github"
+ }
+ },
+ "flake-utils": {
+ "inputs": {
+ "systems": "systems"
+ },
+ "locked": {
+ "lastModified": 1685518550,
+ "narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=",
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef",
+ "type": "github"
+ },
+ "original": {
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "type": "github"
+ }
+ },
+ "gitignore": {
+ "inputs": {
+ "nixpkgs": [
+ "devenv",
+ "pre-commit-hooks",
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1660459072,
+ "narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=",
+ "owner": "hercules-ci",
+ "repo": "gitignore.nix",
+ "rev": "a20de23b925fd8264fd7fad6454652e142fd7f73",
+ "type": "github"
+ },
+ "original": {
+ "owner": "hercules-ci",
+ "repo": "gitignore.nix",
+ "type": "github"
+ }
+ },
+ "lowdown-src": {
+ "flake": false,
+ "locked": {
+ "lastModified": 1633514407,
+ "narHash": "sha256-Dw32tiMjdK9t3ETl5fzGrutQTzh2rufgZV4A/BbxuD4=",
+ "owner": "kristapsdz",
+ "repo": "lowdown",
+ "rev": "d2c2b44ff6c27b936ec27358a2653caaef8f73b8",
+ "type": "github"
+ },
+ "original": {
+ "owner": "kristapsdz",
+ "repo": "lowdown",
+ "type": "github"
+ }
+ },
+ "nix": {
+ "inputs": {
+ "lowdown-src": "lowdown-src",
+ "nixpkgs": [
+ "devenv",
+ "nixpkgs"
+ ],
+ "nixpkgs-regression": "nixpkgs-regression"
+ },
+ "locked": {
+ "lastModified": 1676545802,
+ "narHash": "sha256-EK4rZ+Hd5hsvXnzSzk2ikhStJnD63odF7SzsQ8CuSPU=",
+ "owner": "domenkozar",
+ "repo": "nix",
+ "rev": "7c91803598ffbcfe4a55c44ac6d49b2cf07a527f",
+ "type": "github"
+ },
+ "original": {
+ "owner": "domenkozar",
+ "ref": "relaxed-flakes",
+ "repo": "nix",
+ "type": "github"
+ }
+ },
+ "nixpkgs": {
+ "locked": {
+ "lastModified": 1678875422,
+ "narHash": "sha256-T3o6NcQPwXjxJMn2shz86Chch4ljXgZn746c2caGxd8=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "126f49a01de5b7e35a43fd43f891ecf6d3a51459",
+ "type": "github"
+ },
+ "original": {
+ "owner": "NixOS",
+ "ref": "nixpkgs-unstable",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "nixpkgs-lib": {
+ "locked": {
+ "dir": "lib",
+ "lastModified": 1693471703,
+ "narHash": "sha256-0l03ZBL8P1P6z8MaSDS/MvuU8E75rVxe5eE1N6gxeTo=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "3e52e76b70d5508f3cec70b882a29199f4d1ee85",
+ "type": "github"
+ },
+ "original": {
+ "dir": "lib",
+ "owner": "NixOS",
+ "ref": "nixos-unstable",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "nixpkgs-regression": {
+ "locked": {
+ "lastModified": 1643052045,
+ "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
+ "type": "github"
+ },
+ "original": {
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
+ "type": "github"
+ }
+ },
+ "nixpkgs-stable": {
+ "locked": {
+ "lastModified": 1685801374,
+ "narHash": "sha256-otaSUoFEMM+LjBI1XL/xGB5ao6IwnZOXc47qhIgJe8U=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "c37ca420157f4abc31e26f436c1145f8951ff373",
+ "type": "github"
+ },
+ "original": {
+ "owner": "NixOS",
+ "ref": "nixos-23.05",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "nixpkgs_2": {
+ "locked": {
+ "lastModified": 1694343207,
+ "narHash": "sha256-jWi7OwFxU5Owi4k2JmiL1sa/OuBCQtpaAesuj5LXC8w=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "78058d810644f5ed276804ce7ea9e82d92bee293",
+ "type": "github"
+ },
+ "original": {
+ "owner": "NixOS",
+ "ref": "nixpkgs-unstable",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "pre-commit-hooks": {
+ "inputs": {
+ "flake-compat": [
+ "devenv",
+ "flake-compat"
+ ],
+ "flake-utils": "flake-utils",
+ "gitignore": "gitignore",
+ "nixpkgs": [
+ "devenv",
+ "nixpkgs"
+ ],
+ "nixpkgs-stable": "nixpkgs-stable"
+ },
+ "locked": {
+ "lastModified": 1688056373,
+ "narHash": "sha256-2+SDlNRTKsgo3LBRiMUcoEUb6sDViRNQhzJquZ4koOI=",
+ "owner": "cachix",
+ "repo": "pre-commit-hooks.nix",
+ "rev": "5843cf069272d92b60c3ed9e55b7a8989c01d4c7",
+ "type": "github"
+ },
+ "original": {
+ "owner": "cachix",
+ "repo": "pre-commit-hooks.nix",
+ "type": "github"
+ }
+ },
+ "root": {
+ "inputs": {
+ "devenv": "devenv",
+ "flake-parts": "flake-parts",
+ "nixpkgs": "nixpkgs_2"
+ }
+ },
+ "systems": {
+ "locked": {
+ "lastModified": 1681028828,
+ "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+ "owner": "nix-systems",
+ "repo": "default",
+ "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-systems",
+ "repo": "default",
+ "type": "github"
+ }
+ }
+ },
+ "root": "root",
+ "version": 7
+}
diff --git a/vendor/github.com/sagikazarmark/locafero/flake.nix b/vendor/github.com/sagikazarmark/locafero/flake.nix
new file mode 100644
index 00000000..209ecf28
--- /dev/null
+++ b/vendor/github.com/sagikazarmark/locafero/flake.nix
@@ -0,0 +1,47 @@
+{
+ description = "Finder library for Afero";
+
+ inputs = {
+ nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
+ flake-parts.url = "github:hercules-ci/flake-parts";
+ devenv.url = "github:cachix/devenv";
+ };
+
+ outputs = inputs@{ flake-parts, ... }:
+ flake-parts.lib.mkFlake { inherit inputs; } {
+ imports = [
+ inputs.devenv.flakeModule
+ ];
+
+ systems = [ "x86_64-linux" "aarch64-darwin" ];
+
+ perSystem = { config, self', inputs', pkgs, system, ... }: rec {
+ devenv.shells = {
+ default = {
+ languages = {
+ go.enable = true;
+ };
+
+ packages = with pkgs; [
+ just
+
+ golangci-lint
+ ];
+
+ # https://github.com/cachix/devenv/issues/528#issuecomment-1556108767
+ containers = pkgs.lib.mkForce { };
+ };
+
+ ci = devenv.shells.default;
+
+ ci_1_20 = {
+ imports = [ devenv.shells.ci ];
+
+ languages = {
+ go.package = pkgs.go_1_20;
+ };
+ };
+ };
+ };
+ };
+}
diff --git a/vendor/github.com/sagikazarmark/locafero/helpers.go b/vendor/github.com/sagikazarmark/locafero/helpers.go
new file mode 100644
index 00000000..05b43448
--- /dev/null
+++ b/vendor/github.com/sagikazarmark/locafero/helpers.go
@@ -0,0 +1,41 @@
+package locafero
+
+import "fmt"
+
+// NameWithExtensions creates a list of names from a base name and a list of extensions.
+//
+// TODO: find a better name for this function.
+func NameWithExtensions(baseName string, extensions ...string) []string {
+ var names []string
+
+ if baseName == "" {
+ return names
+ }
+
+ for _, ext := range extensions {
+ if ext == "" {
+ continue
+ }
+
+ names = append(names, fmt.Sprintf("%s.%s", baseName, ext))
+ }
+
+ return names
+}
+
+// NameWithOptionalExtensions creates a list of names from a base name and a list of extensions,
+// plus it adds the base name (without any extensions) to the end of the list.
+//
+// TODO: find a better name for this function.
+func NameWithOptionalExtensions(baseName string, extensions ...string) []string {
+ var names []string
+
+ if baseName == "" {
+ return names
+ }
+
+ names = NameWithExtensions(baseName, extensions...)
+ names = append(names, baseName)
+
+ return names
+}
diff --git a/vendor/github.com/sagikazarmark/locafero/justfile b/vendor/github.com/sagikazarmark/locafero/justfile
new file mode 100644
index 00000000..00a88850
--- /dev/null
+++ b/vendor/github.com/sagikazarmark/locafero/justfile
@@ -0,0 +1,11 @@
+default:
+ just --list
+
+test:
+ go test -race -v ./...
+
+lint:
+ golangci-lint run
+
+fmt:
+ golangci-lint run --fix
diff --git a/vendor/github.com/sagikazarmark/slog-shim/.editorconfig b/vendor/github.com/sagikazarmark/slog-shim/.editorconfig
new file mode 100644
index 00000000..1fb0e1be
--- /dev/null
+++ b/vendor/github.com/sagikazarmark/slog-shim/.editorconfig
@@ -0,0 +1,18 @@
+root = true
+
+[*]
+charset = utf-8
+end_of_line = lf
+indent_size = 4
+indent_style = space
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.nix]
+indent_size = 2
+
+[{Makefile,*.mk}]
+indent_style = tab
+
+[Taskfile.yaml]
+indent_size = 2
diff --git a/vendor/github.com/sagikazarmark/slog-shim/.envrc b/vendor/github.com/sagikazarmark/slog-shim/.envrc
new file mode 100644
index 00000000..3ce7171a
--- /dev/null
+++ b/vendor/github.com/sagikazarmark/slog-shim/.envrc
@@ -0,0 +1,4 @@
+if ! has nix_direnv_version || ! nix_direnv_version 2.3.0; then
+ source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/2.3.0/direnvrc" "sha256-Dmd+j63L84wuzgyjITIfSxSD57Tx7v51DMxVZOsiUD8="
+fi
+use flake . --impure
diff --git a/vendor/github.com/sagikazarmark/slog-shim/.gitignore b/vendor/github.com/sagikazarmark/slog-shim/.gitignore
new file mode 100644
index 00000000..dc6d8b58
--- /dev/null
+++ b/vendor/github.com/sagikazarmark/slog-shim/.gitignore
@@ -0,0 +1,4 @@
+/.devenv/
+/.direnv/
+/.task/
+/build/
diff --git a/vendor/github.com/sagikazarmark/slog-shim/LICENSE b/vendor/github.com/sagikazarmark/slog-shim/LICENSE
new file mode 100644
index 00000000..6a66aea5
--- /dev/null
+++ b/vendor/github.com/sagikazarmark/slog-shim/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2009 The Go Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/github.com/sagikazarmark/slog-shim/README.md b/vendor/github.com/sagikazarmark/slog-shim/README.md
new file mode 100644
index 00000000..1f5be85e
--- /dev/null
+++ b/vendor/github.com/sagikazarmark/slog-shim/README.md
@@ -0,0 +1,81 @@
+# [slog](https://pkg.go.dev/log/slog) shim
+
+[](https://github.com/sagikazarmark/slog-shim/actions/workflows/ci.yaml)
+[](https://pkg.go.dev/mod/github.com/sagikazarmark/slog-shim)
+
+[](https://builtwithnix.org)
+
+Go 1.21 introduced a [new structured logging package](https://golang.org/doc/go1.21#slog), `log/slog`, to the standard library.
+Although it's been eagerly anticipated by many, widespread adoption isn't expected to occur immediately,
+especially since updating to Go 1.21 is a decision that most libraries won't make overnight.
+
+Before this package was added to the standard library, there was an _experimental_ version available at [golang.org/x/exp/slog](https://pkg.go.dev/golang.org/x/exp/slog).
+While it's generally advised against using experimental packages in production,
+this one served as a sort of backport package for the last few years,
+incorporating new features before they were added to the standard library (like `slices`, `maps` or `errors`).
+
+This package serves as a bridge, helping libraries integrate slog in a backward-compatible way without having to immediately update their Go version requirement to 1.21. On Go 1.21 (and above), it acts as a drop-in replacement for `log/slog`, while below 1.21 it falls back to `golang.org/x/exp/slog`.
+
+**How does it achieve backwards compatibility?**
+
+Although there's no consensus on whether dropping support for older Go versions is considered backward compatible, a majority seems to believe it is.
+(I don't have scientific proof for this, but it's based on conversations with various individuals across different channels.)
+
+This package adheres to that interpretation of backward compatibility. On Go 1.21, the shim uses type aliases to offer the same API as `slog/log`.
+Once a library upgrades its version requirement to Go 1.21, it should be able to discard this shim and use `log/slog` directly.
+
+For older Go versions, the library might become unstable after removing the shim.
+However, since those older versions are no longer supported, the promise of backward compatibility remains intact.
+
+## Installation
+
+```shell
+go get github.com/sagikazarmark/slog-shim
+```
+
+## Usage
+
+Import this package into your library and use it in your public API:
+
+```go
+package mylib
+
+import slog "github.com/sagikazarmark/slog-shim"
+
+func New(logger *slog.Logger) MyLib {
+ // ...
+}
+```
+
+When using the library, clients can either use `log/slog` (when on Go 1.21) or `golang.org/x/exp/slog` (below Go 1.21):
+
+```go
+package main
+
+import "log/slog"
+
+// OR
+
+import "golang.org/x/exp/slog"
+
+mylib.New(slog.Default())
+```
+
+**Make sure consumers are aware that your API behaves differently on different Go versions.**
+
+Once you bump your Go version requirement to Go 1.21, you can drop the shim entirely from your code:
+
+```diff
+package mylib
+
+- import slog "github.com/sagikazarmark/slog-shim"
++ import "log/slog"
+
+func New(logger *slog.Logger) MyLib {
+ // ...
+}
+```
+
+## License
+
+The project is licensed under a [BSD-style license](LICENSE).
diff --git a/vendor/github.com/sagikazarmark/slog-shim/attr.go b/vendor/github.com/sagikazarmark/slog-shim/attr.go
new file mode 100644
index 00000000..89608bf3
--- /dev/null
+++ b/vendor/github.com/sagikazarmark/slog-shim/attr.go
@@ -0,0 +1,74 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build go1.21
+
+package slog
+
+import (
+ "log/slog"
+ "time"
+)
+
+// An Attr is a key-value pair.
+type Attr = slog.Attr
+
+// String returns an Attr for a string value.
+func String(key, value string) Attr {
+ return slog.String(key, value)
+}
+
+// Int64 returns an Attr for an int64.
+func Int64(key string, value int64) Attr {
+ return slog.Int64(key, value)
+}
+
+// Int converts an int to an int64 and returns
+// an Attr with that value.
+func Int(key string, value int) Attr {
+ return slog.Int(key, value)
+}
+
+// Uint64 returns an Attr for a uint64.
+func Uint64(key string, v uint64) Attr {
+ return slog.Uint64(key, v)
+}
+
+// Float64 returns an Attr for a floating-point number.
+func Float64(key string, v float64) Attr {
+ return slog.Float64(key, v)
+}
+
+// Bool returns an Attr for a bool.
+func Bool(key string, v bool) Attr {
+ return slog.Bool(key, v)
+}
+
+// Time returns an Attr for a time.Time.
+// It discards the monotonic portion.
+func Time(key string, v time.Time) Attr {
+ return slog.Time(key, v)
+}
+
+// Duration returns an Attr for a time.Duration.
+func Duration(key string, v time.Duration) Attr {
+ return slog.Duration(key, v)
+}
+
+// Group returns an Attr for a Group Value.
+// The first argument is the key; the remaining arguments
+// are converted to Attrs as in [Logger.Log].
+//
+// Use Group to collect several key-value pairs under a single
+// key on a log line, or as the result of LogValue
+// in order to log a single value as multiple Attrs.
+func Group(key string, args ...any) Attr {
+ return slog.Group(key, args...)
+}
+
+// Any returns an Attr for the supplied value.
+// See [Value.AnyValue] for how values are treated.
+func Any(key string, value any) Attr {
+ return slog.Any(key, value)
+}
diff --git a/vendor/github.com/sagikazarmark/slog-shim/attr_120.go b/vendor/github.com/sagikazarmark/slog-shim/attr_120.go
new file mode 100644
index 00000000..b6648133
--- /dev/null
+++ b/vendor/github.com/sagikazarmark/slog-shim/attr_120.go
@@ -0,0 +1,75 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build !go1.21
+
+package slog
+
+import (
+ "time"
+
+ "golang.org/x/exp/slog"
+)
+
+// An Attr is a key-value pair.
+type Attr = slog.Attr
+
+// String returns an Attr for a string value.
+func String(key, value string) Attr {
+ return slog.String(key, value)
+}
+
+// Int64 returns an Attr for an int64.
+func Int64(key string, value int64) Attr {
+ return slog.Int64(key, value)
+}
+
+// Int converts an int to an int64 and returns
+// an Attr with that value.
+func Int(key string, value int) Attr {
+ return slog.Int(key, value)
+}
+
+// Uint64 returns an Attr for a uint64.
+func Uint64(key string, v uint64) Attr {
+ return slog.Uint64(key, v)
+}
+
+// Float64 returns an Attr for a floating-point number.
+func Float64(key string, v float64) Attr {
+ return slog.Float64(key, v)
+}
+
+// Bool returns an Attr for a bool.
+func Bool(key string, v bool) Attr {
+ return slog.Bool(key, v)
+}
+
+// Time returns an Attr for a time.Time.
+// It discards the monotonic portion.
+func Time(key string, v time.Time) Attr {
+ return slog.Time(key, v)
+}
+
+// Duration returns an Attr for a time.Duration.
+func Duration(key string, v time.Duration) Attr {
+ return slog.Duration(key, v)
+}
+
+// Group returns an Attr for a Group Value.
+// The first argument is the key; the remaining arguments
+// are converted to Attrs as in [Logger.Log].
+//
+// Use Group to collect several key-value pairs under a single
+// key on a log line, or as the result of LogValue
+// in order to log a single value as multiple Attrs.
+func Group(key string, args ...any) Attr {
+ return slog.Group(key, args...)
+}
+
+// Any returns an Attr for the supplied value.
+// See [Value.AnyValue] for how values are treated.
+func Any(key string, value any) Attr {
+ return slog.Any(key, value)
+}
diff --git a/vendor/github.com/sagikazarmark/slog-shim/flake.lock b/vendor/github.com/sagikazarmark/slog-shim/flake.lock
new file mode 100644
index 00000000..7e8898e9
--- /dev/null
+++ b/vendor/github.com/sagikazarmark/slog-shim/flake.lock
@@ -0,0 +1,273 @@
+{
+ "nodes": {
+ "devenv": {
+ "inputs": {
+ "flake-compat": "flake-compat",
+ "nix": "nix",
+ "nixpkgs": "nixpkgs",
+ "pre-commit-hooks": "pre-commit-hooks"
+ },
+ "locked": {
+ "lastModified": 1694097209,
+ "narHash": "sha256-gQmBjjxeSyySjbh0yQVBKApo2KWIFqqbRUvG+Fa+QpM=",
+ "owner": "cachix",
+ "repo": "devenv",
+ "rev": "7a8e6a91510efe89d8dcb8e43233f93e86f6b189",
+ "type": "github"
+ },
+ "original": {
+ "owner": "cachix",
+ "repo": "devenv",
+ "type": "github"
+ }
+ },
+ "flake-compat": {
+ "flake": false,
+ "locked": {
+ "lastModified": 1673956053,
+ "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
+ "owner": "edolstra",
+ "repo": "flake-compat",
+ "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
+ "type": "github"
+ },
+ "original": {
+ "owner": "edolstra",
+ "repo": "flake-compat",
+ "type": "github"
+ }
+ },
+ "flake-parts": {
+ "inputs": {
+ "nixpkgs-lib": "nixpkgs-lib"
+ },
+ "locked": {
+ "lastModified": 1693611461,
+ "narHash": "sha256-aPODl8vAgGQ0ZYFIRisxYG5MOGSkIczvu2Cd8Gb9+1Y=",
+ "owner": "hercules-ci",
+ "repo": "flake-parts",
+ "rev": "7f53fdb7bdc5bb237da7fefef12d099e4fd611ca",
+ "type": "github"
+ },
+ "original": {
+ "owner": "hercules-ci",
+ "repo": "flake-parts",
+ "type": "github"
+ }
+ },
+ "flake-utils": {
+ "inputs": {
+ "systems": "systems"
+ },
+ "locked": {
+ "lastModified": 1685518550,
+ "narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=",
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef",
+ "type": "github"
+ },
+ "original": {
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "type": "github"
+ }
+ },
+ "gitignore": {
+ "inputs": {
+ "nixpkgs": [
+ "devenv",
+ "pre-commit-hooks",
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1660459072,
+ "narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=",
+ "owner": "hercules-ci",
+ "repo": "gitignore.nix",
+ "rev": "a20de23b925fd8264fd7fad6454652e142fd7f73",
+ "type": "github"
+ },
+ "original": {
+ "owner": "hercules-ci",
+ "repo": "gitignore.nix",
+ "type": "github"
+ }
+ },
+ "lowdown-src": {
+ "flake": false,
+ "locked": {
+ "lastModified": 1633514407,
+ "narHash": "sha256-Dw32tiMjdK9t3ETl5fzGrutQTzh2rufgZV4A/BbxuD4=",
+ "owner": "kristapsdz",
+ "repo": "lowdown",
+ "rev": "d2c2b44ff6c27b936ec27358a2653caaef8f73b8",
+ "type": "github"
+ },
+ "original": {
+ "owner": "kristapsdz",
+ "repo": "lowdown",
+ "type": "github"
+ }
+ },
+ "nix": {
+ "inputs": {
+ "lowdown-src": "lowdown-src",
+ "nixpkgs": [
+ "devenv",
+ "nixpkgs"
+ ],
+ "nixpkgs-regression": "nixpkgs-regression"
+ },
+ "locked": {
+ "lastModified": 1676545802,
+ "narHash": "sha256-EK4rZ+Hd5hsvXnzSzk2ikhStJnD63odF7SzsQ8CuSPU=",
+ "owner": "domenkozar",
+ "repo": "nix",
+ "rev": "7c91803598ffbcfe4a55c44ac6d49b2cf07a527f",
+ "type": "github"
+ },
+ "original": {
+ "owner": "domenkozar",
+ "ref": "relaxed-flakes",
+ "repo": "nix",
+ "type": "github"
+ }
+ },
+ "nixpkgs": {
+ "locked": {
+ "lastModified": 1678875422,
+ "narHash": "sha256-T3o6NcQPwXjxJMn2shz86Chch4ljXgZn746c2caGxd8=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "126f49a01de5b7e35a43fd43f891ecf6d3a51459",
+ "type": "github"
+ },
+ "original": {
+ "owner": "NixOS",
+ "ref": "nixpkgs-unstable",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "nixpkgs-lib": {
+ "locked": {
+ "dir": "lib",
+ "lastModified": 1693471703,
+ "narHash": "sha256-0l03ZBL8P1P6z8MaSDS/MvuU8E75rVxe5eE1N6gxeTo=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "3e52e76b70d5508f3cec70b882a29199f4d1ee85",
+ "type": "github"
+ },
+ "original": {
+ "dir": "lib",
+ "owner": "NixOS",
+ "ref": "nixos-unstable",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "nixpkgs-regression": {
+ "locked": {
+ "lastModified": 1643052045,
+ "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
+ "type": "github"
+ },
+ "original": {
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
+ "type": "github"
+ }
+ },
+ "nixpkgs-stable": {
+ "locked": {
+ "lastModified": 1685801374,
+ "narHash": "sha256-otaSUoFEMM+LjBI1XL/xGB5ao6IwnZOXc47qhIgJe8U=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "c37ca420157f4abc31e26f436c1145f8951ff373",
+ "type": "github"
+ },
+ "original": {
+ "owner": "NixOS",
+ "ref": "nixos-23.05",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "nixpkgs_2": {
+ "locked": {
+ "lastModified": 1694345580,
+ "narHash": "sha256-BbG0NUxQTz1dN/Y87yPWZc/0Kp/coJ0vM3+7sNa5kUM=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "f002de6834fdde9c864f33c1ec51da7df19cd832",
+ "type": "github"
+ },
+ "original": {
+ "owner": "NixOS",
+ "ref": "master",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "pre-commit-hooks": {
+ "inputs": {
+ "flake-compat": [
+ "devenv",
+ "flake-compat"
+ ],
+ "flake-utils": "flake-utils",
+ "gitignore": "gitignore",
+ "nixpkgs": [
+ "devenv",
+ "nixpkgs"
+ ],
+ "nixpkgs-stable": "nixpkgs-stable"
+ },
+ "locked": {
+ "lastModified": 1688056373,
+ "narHash": "sha256-2+SDlNRTKsgo3LBRiMUcoEUb6sDViRNQhzJquZ4koOI=",
+ "owner": "cachix",
+ "repo": "pre-commit-hooks.nix",
+ "rev": "5843cf069272d92b60c3ed9e55b7a8989c01d4c7",
+ "type": "github"
+ },
+ "original": {
+ "owner": "cachix",
+ "repo": "pre-commit-hooks.nix",
+ "type": "github"
+ }
+ },
+ "root": {
+ "inputs": {
+ "devenv": "devenv",
+ "flake-parts": "flake-parts",
+ "nixpkgs": "nixpkgs_2"
+ }
+ },
+ "systems": {
+ "locked": {
+ "lastModified": 1681028828,
+ "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+ "owner": "nix-systems",
+ "repo": "default",
+ "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-systems",
+ "repo": "default",
+ "type": "github"
+ }
+ }
+ },
+ "root": "root",
+ "version": 7
+}
diff --git a/vendor/github.com/sagikazarmark/slog-shim/flake.nix b/vendor/github.com/sagikazarmark/slog-shim/flake.nix
new file mode 100644
index 00000000..7239bbc2
--- /dev/null
+++ b/vendor/github.com/sagikazarmark/slog-shim/flake.nix
@@ -0,0 +1,57 @@
+{
+ inputs = {
+ # nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
+ nixpkgs.url = "github:NixOS/nixpkgs/master";
+ flake-parts.url = "github:hercules-ci/flake-parts";
+ devenv.url = "github:cachix/devenv";
+ };
+
+ outputs = inputs@{ flake-parts, ... }:
+ flake-parts.lib.mkFlake { inherit inputs; } {
+ imports = [
+ inputs.devenv.flakeModule
+ ];
+
+ systems = [ "x86_64-linux" "x86_64-darwin" "aarch64-darwin" ];
+
+ perSystem = { config, self', inputs', pkgs, system, ... }: rec {
+ devenv.shells = {
+ default = {
+ languages = {
+ go.enable = true;
+ go.package = pkgs.lib.mkDefault pkgs.go_1_21;
+ };
+
+ # https://github.com/cachix/devenv/issues/528#issuecomment-1556108767
+ containers = pkgs.lib.mkForce { };
+ };
+
+ ci = devenv.shells.default;
+
+ ci_1_19 = {
+ imports = [ devenv.shells.ci ];
+
+ languages = {
+ go.package = pkgs.go_1_19;
+ };
+ };
+
+ ci_1_20 = {
+ imports = [ devenv.shells.ci ];
+
+ languages = {
+ go.package = pkgs.go_1_20;
+ };
+ };
+
+ ci_1_21 = {
+ imports = [ devenv.shells.ci ];
+
+ languages = {
+ go.package = pkgs.go_1_21;
+ };
+ };
+ };
+ };
+ };
+}
diff --git a/vendor/github.com/sagikazarmark/slog-shim/handler.go b/vendor/github.com/sagikazarmark/slog-shim/handler.go
new file mode 100644
index 00000000..f55556ae
--- /dev/null
+++ b/vendor/github.com/sagikazarmark/slog-shim/handler.go
@@ -0,0 +1,45 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build go1.21
+
+package slog
+
+import (
+ "log/slog"
+)
+
+// A Handler handles log records produced by a Logger..
+//
+// A typical handler may print log records to standard error,
+// or write them to a file or database, or perhaps augment them
+// with additional attributes and pass them on to another handler.
+//
+// Any of the Handler's methods may be called concurrently with itself
+// or with other methods. It is the responsibility of the Handler to
+// manage this concurrency.
+//
+// Users of the slog package should not invoke Handler methods directly.
+// They should use the methods of [Logger] instead.
+type Handler = slog.Handler
+
+// HandlerOptions are options for a TextHandler or JSONHandler.
+// A zero HandlerOptions consists entirely of default values.
+type HandlerOptions = slog.HandlerOptions
+
+// Keys for "built-in" attributes.
+const (
+ // TimeKey is the key used by the built-in handlers for the time
+ // when the log method is called. The associated Value is a [time.Time].
+ TimeKey = slog.TimeKey
+ // LevelKey is the key used by the built-in handlers for the level
+ // of the log call. The associated value is a [Level].
+ LevelKey = slog.LevelKey
+ // MessageKey is the key used by the built-in handlers for the
+ // message of the log call. The associated value is a string.
+ MessageKey = slog.MessageKey
+ // SourceKey is the key used by the built-in handlers for the source file
+ // and line of the log call. The associated value is a string.
+ SourceKey = slog.SourceKey
+)
diff --git a/vendor/github.com/sagikazarmark/slog-shim/handler_120.go b/vendor/github.com/sagikazarmark/slog-shim/handler_120.go
new file mode 100644
index 00000000..67005757
--- /dev/null
+++ b/vendor/github.com/sagikazarmark/slog-shim/handler_120.go
@@ -0,0 +1,45 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build !go1.21
+
+package slog
+
+import (
+ "golang.org/x/exp/slog"
+)
+
+// A Handler handles log records produced by a Logger..
+//
+// A typical handler may print log records to standard error,
+// or write them to a file or database, or perhaps augment them
+// with additional attributes and pass them on to another handler.
+//
+// Any of the Handler's methods may be called concurrently with itself
+// or with other methods. It is the responsibility of the Handler to
+// manage this concurrency.
+//
+// Users of the slog package should not invoke Handler methods directly.
+// They should use the methods of [Logger] instead.
+type Handler = slog.Handler
+
+// HandlerOptions are options for a TextHandler or JSONHandler.
+// A zero HandlerOptions consists entirely of default values.
+type HandlerOptions = slog.HandlerOptions
+
+// Keys for "built-in" attributes.
+const (
+ // TimeKey is the key used by the built-in handlers for the time
+ // when the log method is called. The associated Value is a [time.Time].
+ TimeKey = slog.TimeKey
+ // LevelKey is the key used by the built-in handlers for the level
+ // of the log call. The associated value is a [Level].
+ LevelKey = slog.LevelKey
+ // MessageKey is the key used by the built-in handlers for the
+ // message of the log call. The associated value is a string.
+ MessageKey = slog.MessageKey
+ // SourceKey is the key used by the built-in handlers for the source file
+ // and line of the log call. The associated value is a string.
+ SourceKey = slog.SourceKey
+)
diff --git a/vendor/github.com/sagikazarmark/slog-shim/json_handler.go b/vendor/github.com/sagikazarmark/slog-shim/json_handler.go
new file mode 100644
index 00000000..7c22bd81
--- /dev/null
+++ b/vendor/github.com/sagikazarmark/slog-shim/json_handler.go
@@ -0,0 +1,23 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build go1.21
+
+package slog
+
+import (
+ "io"
+ "log/slog"
+)
+
+// JSONHandler is a Handler that writes Records to an io.Writer as
+// line-delimited JSON objects.
+type JSONHandler = slog.JSONHandler
+
+// NewJSONHandler creates a JSONHandler that writes to w,
+// using the given options.
+// If opts is nil, the default options are used.
+func NewJSONHandler(w io.Writer, opts *HandlerOptions) *JSONHandler {
+ return slog.NewJSONHandler(w, opts)
+}
diff --git a/vendor/github.com/sagikazarmark/slog-shim/json_handler_120.go b/vendor/github.com/sagikazarmark/slog-shim/json_handler_120.go
new file mode 100644
index 00000000..7b14f10b
--- /dev/null
+++ b/vendor/github.com/sagikazarmark/slog-shim/json_handler_120.go
@@ -0,0 +1,24 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build !go1.21
+
+package slog
+
+import (
+ "io"
+
+ "golang.org/x/exp/slog"
+)
+
+// JSONHandler is a Handler that writes Records to an io.Writer as
+// line-delimited JSON objects.
+type JSONHandler = slog.JSONHandler
+
+// NewJSONHandler creates a JSONHandler that writes to w,
+// using the given options.
+// If opts is nil, the default options are used.
+func NewJSONHandler(w io.Writer, opts *HandlerOptions) *JSONHandler {
+ return slog.NewJSONHandler(w, opts)
+}
diff --git a/vendor/github.com/sagikazarmark/slog-shim/level.go b/vendor/github.com/sagikazarmark/slog-shim/level.go
new file mode 100644
index 00000000..07288cf8
--- /dev/null
+++ b/vendor/github.com/sagikazarmark/slog-shim/level.go
@@ -0,0 +1,61 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build go1.21
+
+package slog
+
+import (
+ "log/slog"
+)
+
+// A Level is the importance or severity of a log event.
+// The higher the level, the more important or severe the event.
+type Level = slog.Level
+
+// Level numbers are inherently arbitrary,
+// but we picked them to satisfy three constraints.
+// Any system can map them to another numbering scheme if it wishes.
+//
+// First, we wanted the default level to be Info, Since Levels are ints, Info is
+// the default value for int, zero.
+//
+// Second, we wanted to make it easy to use levels to specify logger verbosity.
+// Since a larger level means a more severe event, a logger that accepts events
+// with smaller (or more negative) level means a more verbose logger. Logger
+// verbosity is thus the negation of event severity, and the default verbosity
+// of 0 accepts all events at least as severe as INFO.
+//
+// Third, we wanted some room between levels to accommodate schemes with named
+// levels between ours. For example, Google Cloud Logging defines a Notice level
+// between Info and Warn. Since there are only a few of these intermediate
+// levels, the gap between the numbers need not be large. Our gap of 4 matches
+// OpenTelemetry's mapping. Subtracting 9 from an OpenTelemetry level in the
+// DEBUG, INFO, WARN and ERROR ranges converts it to the corresponding slog
+// Level range. OpenTelemetry also has the names TRACE and FATAL, which slog
+// does not. But those OpenTelemetry levels can still be represented as slog
+// Levels by using the appropriate integers.
+//
+// Names for common levels.
+const (
+ LevelDebug Level = slog.LevelDebug
+ LevelInfo Level = slog.LevelInfo
+ LevelWarn Level = slog.LevelWarn
+ LevelError Level = slog.LevelError
+)
+
+// A LevelVar is a Level variable, to allow a Handler level to change
+// dynamically.
+// It implements Leveler as well as a Set method,
+// and it is safe for use by multiple goroutines.
+// The zero LevelVar corresponds to LevelInfo.
+type LevelVar = slog.LevelVar
+
+// A Leveler provides a Level value.
+//
+// As Level itself implements Leveler, clients typically supply
+// a Level value wherever a Leveler is needed, such as in HandlerOptions.
+// Clients who need to vary the level dynamically can provide a more complex
+// Leveler implementation such as *LevelVar.
+type Leveler = slog.Leveler
diff --git a/vendor/github.com/sagikazarmark/slog-shim/level_120.go b/vendor/github.com/sagikazarmark/slog-shim/level_120.go
new file mode 100644
index 00000000..d3feb942
--- /dev/null
+++ b/vendor/github.com/sagikazarmark/slog-shim/level_120.go
@@ -0,0 +1,61 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build !go1.21
+
+package slog
+
+import (
+ "golang.org/x/exp/slog"
+)
+
+// A Level is the importance or severity of a log event.
+// The higher the level, the more important or severe the event.
+type Level = slog.Level
+
+// Level numbers are inherently arbitrary,
+// but we picked them to satisfy three constraints.
+// Any system can map them to another numbering scheme if it wishes.
+//
+// First, we wanted the default level to be Info, Since Levels are ints, Info is
+// the default value for int, zero.
+//
+// Second, we wanted to make it easy to use levels to specify logger verbosity.
+// Since a larger level means a more severe event, a logger that accepts events
+// with smaller (or more negative) level means a more verbose logger. Logger
+// verbosity is thus the negation of event severity, and the default verbosity
+// of 0 accepts all events at least as severe as INFO.
+//
+// Third, we wanted some room between levels to accommodate schemes with named
+// levels between ours. For example, Google Cloud Logging defines a Notice level
+// between Info and Warn. Since there are only a few of these intermediate
+// levels, the gap between the numbers need not be large. Our gap of 4 matches
+// OpenTelemetry's mapping. Subtracting 9 from an OpenTelemetry level in the
+// DEBUG, INFO, WARN and ERROR ranges converts it to the corresponding slog
+// Level range. OpenTelemetry also has the names TRACE and FATAL, which slog
+// does not. But those OpenTelemetry levels can still be represented as slog
+// Levels by using the appropriate integers.
+//
+// Names for common levels.
+const (
+ LevelDebug Level = slog.LevelDebug
+ LevelInfo Level = slog.LevelInfo
+ LevelWarn Level = slog.LevelWarn
+ LevelError Level = slog.LevelError
+)
+
+// A LevelVar is a Level variable, to allow a Handler level to change
+// dynamically.
+// It implements Leveler as well as a Set method,
+// and it is safe for use by multiple goroutines.
+// The zero LevelVar corresponds to LevelInfo.
+type LevelVar = slog.LevelVar
+
+// A Leveler provides a Level value.
+//
+// As Level itself implements Leveler, clients typically supply
+// a Level value wherever a Leveler is needed, such as in HandlerOptions.
+// Clients who need to vary the level dynamically can provide a more complex
+// Leveler implementation such as *LevelVar.
+type Leveler = slog.Leveler
diff --git a/vendor/github.com/sagikazarmark/slog-shim/logger.go b/vendor/github.com/sagikazarmark/slog-shim/logger.go
new file mode 100644
index 00000000..e80036be
--- /dev/null
+++ b/vendor/github.com/sagikazarmark/slog-shim/logger.go
@@ -0,0 +1,98 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build go1.21
+
+package slog
+
+import (
+ "context"
+ "log"
+ "log/slog"
+)
+
+// Default returns the default Logger.
+func Default() *Logger { return slog.Default() }
+
+// SetDefault makes l the default Logger.
+// After this call, output from the log package's default Logger
+// (as with [log.Print], etc.) will be logged at LevelInfo using l's Handler.
+func SetDefault(l *Logger) {
+ slog.SetDefault(l)
+}
+
+// A Logger records structured information about each call to its
+// Log, Debug, Info, Warn, and Error methods.
+// For each call, it creates a Record and passes it to a Handler.
+//
+// To create a new Logger, call [New] or a Logger method
+// that begins "With".
+type Logger = slog.Logger
+
+// New creates a new Logger with the given non-nil Handler.
+func New(h Handler) *Logger {
+ return slog.New(h)
+}
+
+// With calls Logger.With on the default logger.
+func With(args ...any) *Logger {
+ return slog.With(args...)
+}
+
+// NewLogLogger returns a new log.Logger such that each call to its Output method
+// dispatches a Record to the specified handler. The logger acts as a bridge from
+// the older log API to newer structured logging handlers.
+func NewLogLogger(h Handler, level Level) *log.Logger {
+ return slog.NewLogLogger(h, level)
+}
+
+// Debug calls Logger.Debug on the default logger.
+func Debug(msg string, args ...any) {
+ slog.Debug(msg, args...)
+}
+
+// DebugContext calls Logger.DebugContext on the default logger.
+func DebugContext(ctx context.Context, msg string, args ...any) {
+ slog.DebugContext(ctx, msg, args...)
+}
+
+// Info calls Logger.Info on the default logger.
+func Info(msg string, args ...any) {
+ slog.Info(msg, args...)
+}
+
+// InfoContext calls Logger.InfoContext on the default logger.
+func InfoContext(ctx context.Context, msg string, args ...any) {
+ slog.InfoContext(ctx, msg, args...)
+}
+
+// Warn calls Logger.Warn on the default logger.
+func Warn(msg string, args ...any) {
+ slog.Warn(msg, args...)
+}
+
+// WarnContext calls Logger.WarnContext on the default logger.
+func WarnContext(ctx context.Context, msg string, args ...any) {
+ slog.WarnContext(ctx, msg, args...)
+}
+
+// Error calls Logger.Error on the default logger.
+func Error(msg string, args ...any) {
+ slog.Error(msg, args...)
+}
+
+// ErrorContext calls Logger.ErrorContext on the default logger.
+func ErrorContext(ctx context.Context, msg string, args ...any) {
+ slog.ErrorContext(ctx, msg, args...)
+}
+
+// Log calls Logger.Log on the default logger.
+func Log(ctx context.Context, level Level, msg string, args ...any) {
+ slog.Log(ctx, level, msg, args...)
+}
+
+// LogAttrs calls Logger.LogAttrs on the default logger.
+func LogAttrs(ctx context.Context, level Level, msg string, attrs ...Attr) {
+ slog.LogAttrs(ctx, level, msg, attrs...)
+}
diff --git a/vendor/github.com/sagikazarmark/slog-shim/logger_120.go b/vendor/github.com/sagikazarmark/slog-shim/logger_120.go
new file mode 100644
index 00000000..97ebdd5e
--- /dev/null
+++ b/vendor/github.com/sagikazarmark/slog-shim/logger_120.go
@@ -0,0 +1,99 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build !go1.21
+
+package slog
+
+import (
+ "context"
+ "log"
+
+ "golang.org/x/exp/slog"
+)
+
+// Default returns the default Logger.
+func Default() *Logger { return slog.Default() }
+
+// SetDefault makes l the default Logger.
+// After this call, output from the log package's default Logger
+// (as with [log.Print], etc.) will be logged at LevelInfo using l's Handler.
+func SetDefault(l *Logger) {
+ slog.SetDefault(l)
+}
+
+// A Logger records structured information about each call to its
+// Log, Debug, Info, Warn, and Error methods.
+// For each call, it creates a Record and passes it to a Handler.
+//
+// To create a new Logger, call [New] or a Logger method
+// that begins "With".
+type Logger = slog.Logger
+
+// New creates a new Logger with the given non-nil Handler.
+func New(h Handler) *Logger {
+ return slog.New(h)
+}
+
+// With calls Logger.With on the default logger.
+func With(args ...any) *Logger {
+ return slog.With(args...)
+}
+
+// NewLogLogger returns a new log.Logger such that each call to its Output method
+// dispatches a Record to the specified handler. The logger acts as a bridge from
+// the older log API to newer structured logging handlers.
+func NewLogLogger(h Handler, level Level) *log.Logger {
+ return slog.NewLogLogger(h, level)
+}
+
+// Debug calls Logger.Debug on the default logger.
+func Debug(msg string, args ...any) {
+ slog.Debug(msg, args...)
+}
+
+// DebugContext calls Logger.DebugContext on the default logger.
+func DebugContext(ctx context.Context, msg string, args ...any) {
+ slog.DebugContext(ctx, msg, args...)
+}
+
+// Info calls Logger.Info on the default logger.
+func Info(msg string, args ...any) {
+ slog.Info(msg, args...)
+}
+
+// InfoContext calls Logger.InfoContext on the default logger.
+func InfoContext(ctx context.Context, msg string, args ...any) {
+ slog.InfoContext(ctx, msg, args...)
+}
+
+// Warn calls Logger.Warn on the default logger.
+func Warn(msg string, args ...any) {
+ slog.Warn(msg, args...)
+}
+
+// WarnContext calls Logger.WarnContext on the default logger.
+func WarnContext(ctx context.Context, msg string, args ...any) {
+ slog.WarnContext(ctx, msg, args...)
+}
+
+// Error calls Logger.Error on the default logger.
+func Error(msg string, args ...any) {
+ slog.Error(msg, args...)
+}
+
+// ErrorContext calls Logger.ErrorContext on the default logger.
+func ErrorContext(ctx context.Context, msg string, args ...any) {
+ slog.ErrorContext(ctx, msg, args...)
+}
+
+// Log calls Logger.Log on the default logger.
+func Log(ctx context.Context, level Level, msg string, args ...any) {
+ slog.Log(ctx, level, msg, args...)
+}
+
+// LogAttrs calls Logger.LogAttrs on the default logger.
+func LogAttrs(ctx context.Context, level Level, msg string, attrs ...Attr) {
+ slog.LogAttrs(ctx, level, msg, attrs...)
+}
diff --git a/vendor/github.com/sagikazarmark/slog-shim/record.go b/vendor/github.com/sagikazarmark/slog-shim/record.go
new file mode 100644
index 00000000..85ad1f78
--- /dev/null
+++ b/vendor/github.com/sagikazarmark/slog-shim/record.go
@@ -0,0 +1,31 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build go1.21
+
+package slog
+
+import (
+ "log/slog"
+ "time"
+)
+
+// A Record holds information about a log event.
+// Copies of a Record share state.
+// Do not modify a Record after handing out a copy to it.
+// Call [NewRecord] to create a new Record.
+// Use [Record.Clone] to create a copy with no shared state.
+type Record = slog.Record
+
+// NewRecord creates a Record from the given arguments.
+// Use [Record.AddAttrs] to add attributes to the Record.
+//
+// NewRecord is intended for logging APIs that want to support a [Handler] as
+// a backend.
+func NewRecord(t time.Time, level Level, msg string, pc uintptr) Record {
+ return slog.NewRecord(t, level, msg, pc)
+}
+
+// Source describes the location of a line of source code.
+type Source = slog.Source
diff --git a/vendor/github.com/sagikazarmark/slog-shim/record_120.go b/vendor/github.com/sagikazarmark/slog-shim/record_120.go
new file mode 100644
index 00000000..c2eaf4e7
--- /dev/null
+++ b/vendor/github.com/sagikazarmark/slog-shim/record_120.go
@@ -0,0 +1,32 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build !go1.21
+
+package slog
+
+import (
+ "time"
+
+ "golang.org/x/exp/slog"
+)
+
+// A Record holds information about a log event.
+// Copies of a Record share state.
+// Do not modify a Record after handing out a copy to it.
+// Call [NewRecord] to create a new Record.
+// Use [Record.Clone] to create a copy with no shared state.
+type Record = slog.Record
+
+// NewRecord creates a Record from the given arguments.
+// Use [Record.AddAttrs] to add attributes to the Record.
+//
+// NewRecord is intended for logging APIs that want to support a [Handler] as
+// a backend.
+func NewRecord(t time.Time, level Level, msg string, pc uintptr) Record {
+ return slog.NewRecord(t, level, msg, pc)
+}
+
+// Source describes the location of a line of source code.
+type Source = slog.Source
diff --git a/vendor/github.com/sagikazarmark/slog-shim/text_handler.go b/vendor/github.com/sagikazarmark/slog-shim/text_handler.go
new file mode 100644
index 00000000..45f6cfcb
--- /dev/null
+++ b/vendor/github.com/sagikazarmark/slog-shim/text_handler.go
@@ -0,0 +1,23 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build go1.21
+
+package slog
+
+import (
+ "io"
+ "log/slog"
+)
+
+// TextHandler is a Handler that writes Records to an io.Writer as a
+// sequence of key=value pairs separated by spaces and followed by a newline.
+type TextHandler = slog.TextHandler
+
+// NewTextHandler creates a TextHandler that writes to w,
+// using the given options.
+// If opts is nil, the default options are used.
+func NewTextHandler(w io.Writer, opts *HandlerOptions) *TextHandler {
+ return slog.NewTextHandler(w, opts)
+}
diff --git a/vendor/github.com/sagikazarmark/slog-shim/text_handler_120.go b/vendor/github.com/sagikazarmark/slog-shim/text_handler_120.go
new file mode 100644
index 00000000..a69d63cc
--- /dev/null
+++ b/vendor/github.com/sagikazarmark/slog-shim/text_handler_120.go
@@ -0,0 +1,24 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build !go1.21
+
+package slog
+
+import (
+ "io"
+
+ "golang.org/x/exp/slog"
+)
+
+// TextHandler is a Handler that writes Records to an io.Writer as a
+// sequence of key=value pairs separated by spaces and followed by a newline.
+type TextHandler = slog.TextHandler
+
+// NewTextHandler creates a TextHandler that writes to w,
+// using the given options.
+// If opts is nil, the default options are used.
+func NewTextHandler(w io.Writer, opts *HandlerOptions) *TextHandler {
+ return slog.NewTextHandler(w, opts)
+}
diff --git a/vendor/github.com/sagikazarmark/slog-shim/value.go b/vendor/github.com/sagikazarmark/slog-shim/value.go
new file mode 100644
index 00000000..61173eb9
--- /dev/null
+++ b/vendor/github.com/sagikazarmark/slog-shim/value.go
@@ -0,0 +1,109 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build go1.21
+
+package slog
+
+import (
+ "log/slog"
+ "time"
+)
+
+// A Value can represent any Go value, but unlike type any,
+// it can represent most small values without an allocation.
+// The zero Value corresponds to nil.
+type Value = slog.Value
+
+// Kind is the kind of a Value.
+type Kind = slog.Kind
+
+// The following list is sorted alphabetically, but it's also important that
+// KindAny is 0 so that a zero Value represents nil.
+const (
+ KindAny = slog.KindAny
+ KindBool = slog.KindBool
+ KindDuration = slog.KindDuration
+ KindFloat64 = slog.KindFloat64
+ KindInt64 = slog.KindInt64
+ KindString = slog.KindString
+ KindTime = slog.KindTime
+ KindUint64 = slog.KindUint64
+ KindGroup = slog.KindGroup
+ KindLogValuer = slog.KindLogValuer
+)
+
+//////////////// Constructors
+
+// StringValue returns a new Value for a string.
+func StringValue(value string) Value {
+ return slog.StringValue(value)
+}
+
+// IntValue returns a Value for an int.
+func IntValue(v int) Value {
+ return slog.IntValue(v)
+}
+
+// Int64Value returns a Value for an int64.
+func Int64Value(v int64) Value {
+ return slog.Int64Value(v)
+}
+
+// Uint64Value returns a Value for a uint64.
+func Uint64Value(v uint64) Value {
+ return slog.Uint64Value(v)
+}
+
+// Float64Value returns a Value for a floating-point number.
+func Float64Value(v float64) Value {
+ return slog.Float64Value(v)
+}
+
+// BoolValue returns a Value for a bool.
+func BoolValue(v bool) Value {
+ return slog.BoolValue(v)
+}
+
+// TimeValue returns a Value for a time.Time.
+// It discards the monotonic portion.
+func TimeValue(v time.Time) Value {
+ return slog.TimeValue(v)
+}
+
+// DurationValue returns a Value for a time.Duration.
+func DurationValue(v time.Duration) Value {
+ return slog.DurationValue(v)
+}
+
+// GroupValue returns a new Value for a list of Attrs.
+// The caller must not subsequently mutate the argument slice.
+func GroupValue(as ...Attr) Value {
+ return slog.GroupValue(as...)
+}
+
+// AnyValue returns a Value for the supplied value.
+//
+// If the supplied value is of type Value, it is returned
+// unmodified.
+//
+// Given a value of one of Go's predeclared string, bool, or
+// (non-complex) numeric types, AnyValue returns a Value of kind
+// String, Bool, Uint64, Int64, or Float64. The width of the
+// original numeric type is not preserved.
+//
+// Given a time.Time or time.Duration value, AnyValue returns a Value of kind
+// KindTime or KindDuration. The monotonic time is not preserved.
+//
+// For nil, or values of all other types, including named types whose
+// underlying type is numeric, AnyValue returns a value of kind KindAny.
+func AnyValue(v any) Value {
+ return slog.AnyValue(v)
+}
+
+// A LogValuer is any Go value that can convert itself into a Value for logging.
+//
+// This mechanism may be used to defer expensive operations until they are
+// needed, or to expand a single value into a sequence of components.
+type LogValuer = slog.LogValuer
diff --git a/vendor/github.com/sagikazarmark/slog-shim/value_120.go b/vendor/github.com/sagikazarmark/slog-shim/value_120.go
new file mode 100644
index 00000000..0f9f871e
--- /dev/null
+++ b/vendor/github.com/sagikazarmark/slog-shim/value_120.go
@@ -0,0 +1,110 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build !go1.21
+
+package slog
+
+import (
+ "time"
+
+ "golang.org/x/exp/slog"
+)
+
+// A Value can represent any Go value, but unlike type any,
+// it can represent most small values without an allocation.
+// The zero Value corresponds to nil.
+type Value = slog.Value
+
+// Kind is the kind of a Value.
+type Kind = slog.Kind
+
+// The following list is sorted alphabetically, but it's also important that
+// KindAny is 0 so that a zero Value represents nil.
+const (
+ KindAny = slog.KindAny
+ KindBool = slog.KindBool
+ KindDuration = slog.KindDuration
+ KindFloat64 = slog.KindFloat64
+ KindInt64 = slog.KindInt64
+ KindString = slog.KindString
+ KindTime = slog.KindTime
+ KindUint64 = slog.KindUint64
+ KindGroup = slog.KindGroup
+ KindLogValuer = slog.KindLogValuer
+)
+
+//////////////// Constructors
+
+// StringValue returns a new Value for a string.
+func StringValue(value string) Value {
+ return slog.StringValue(value)
+}
+
+// IntValue returns a Value for an int.
+func IntValue(v int) Value {
+ return slog.IntValue(v)
+}
+
+// Int64Value returns a Value for an int64.
+func Int64Value(v int64) Value {
+ return slog.Int64Value(v)
+}
+
+// Uint64Value returns a Value for a uint64.
+func Uint64Value(v uint64) Value {
+ return slog.Uint64Value(v)
+}
+
+// Float64Value returns a Value for a floating-point number.
+func Float64Value(v float64) Value {
+ return slog.Float64Value(v)
+}
+
+// BoolValue returns a Value for a bool.
+func BoolValue(v bool) Value {
+ return slog.BoolValue(v)
+}
+
+// TimeValue returns a Value for a time.Time.
+// It discards the monotonic portion.
+func TimeValue(v time.Time) Value {
+ return slog.TimeValue(v)
+}
+
+// DurationValue returns a Value for a time.Duration.
+func DurationValue(v time.Duration) Value {
+ return slog.DurationValue(v)
+}
+
+// GroupValue returns a new Value for a list of Attrs.
+// The caller must not subsequently mutate the argument slice.
+func GroupValue(as ...Attr) Value {
+ return slog.GroupValue(as...)
+}
+
+// AnyValue returns a Value for the supplied value.
+//
+// If the supplied value is of type Value, it is returned
+// unmodified.
+//
+// Given a value of one of Go's predeclared string, bool, or
+// (non-complex) numeric types, AnyValue returns a Value of kind
+// String, Bool, Uint64, Int64, or Float64. The width of the
+// original numeric type is not preserved.
+//
+// Given a time.Time or time.Duration value, AnyValue returns a Value of kind
+// KindTime or KindDuration. The monotonic time is not preserved.
+//
+// For nil, or values of all other types, including named types whose
+// underlying type is numeric, AnyValue returns a value of kind KindAny.
+func AnyValue(v any) Value {
+ return slog.AnyValue(v)
+}
+
+// A LogValuer is any Go value that can convert itself into a Value for logging.
+//
+// This mechanism may be used to defer expensive operations until they are
+// needed, or to expand a single value into a sequence of components.
+type LogValuer = slog.LogValuer
diff --git a/vendor/github.com/sergi/go-diff/AUTHORS b/vendor/github.com/sergi/go-diff/AUTHORS
new file mode 100644
index 00000000..2d7bb2bf
--- /dev/null
+++ b/vendor/github.com/sergi/go-diff/AUTHORS
@@ -0,0 +1,25 @@
+# This is the official list of go-diff authors for copyright purposes.
+# This file is distinct from the CONTRIBUTORS files.
+# See the latter for an explanation.
+
+# Names should be added to this file as
+# Name or Organization
+# The email address is not required for organizations.
+
+# Please keep the list sorted.
+
+Danny Yoo
+James Kolb
+Jonathan Amsterdam
+Markus Zimmermann
+Matt Kovars
+Örjan Persson
+Osman Masood
+Robert Carlsen
+Rory Flynn
+Sergi Mansilla
+Shatrugna Sadhu
+Shawn Smith
+Stas Maksimov
+Tor Arvid Lund
+Zac Bergquist
diff --git a/vendor/github.com/sergi/go-diff/CONTRIBUTORS b/vendor/github.com/sergi/go-diff/CONTRIBUTORS
new file mode 100644
index 00000000..369e3d55
--- /dev/null
+++ b/vendor/github.com/sergi/go-diff/CONTRIBUTORS
@@ -0,0 +1,32 @@
+# This is the official list of people who can contribute
+# (and typically have contributed) code to the go-diff
+# repository.
+#
+# The AUTHORS file lists the copyright holders; this file
+# lists people. For example, ACME Inc. employees would be listed here
+# but not in AUTHORS, because ACME Inc. would hold the copyright.
+#
+# When adding J Random Contributor's name to this file,
+# either J's name or J's organization's name should be
+# added to the AUTHORS file.
+#
+# Names should be added to this file like so:
+# Name
+#
+# Please keep the list sorted.
+
+Danny Yoo
+James Kolb
+Jonathan Amsterdam
+Markus Zimmermann
+Matt Kovars
+Örjan Persson
+Osman Masood
+Robert Carlsen
+Rory Flynn
+Sergi Mansilla
+Shatrugna Sadhu
+Shawn Smith
+Stas Maksimov
+Tor Arvid Lund
+Zac Bergquist
diff --git a/vendor/github.com/sergi/go-diff/LICENSE b/vendor/github.com/sergi/go-diff/LICENSE
new file mode 100644
index 00000000..937942c2
--- /dev/null
+++ b/vendor/github.com/sergi/go-diff/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2012-2016 The go-diff Authors. All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
diff --git a/vendor/github.com/sergi/go-diff/diffmatchpatch/diff.go b/vendor/github.com/sergi/go-diff/diffmatchpatch/diff.go
new file mode 100644
index 00000000..915d5090
--- /dev/null
+++ b/vendor/github.com/sergi/go-diff/diffmatchpatch/diff.go
@@ -0,0 +1,1347 @@
+// Copyright (c) 2012-2016 The go-diff authors. All rights reserved.
+// https://github.com/sergi/go-diff
+// See the included LICENSE file for license details.
+//
+// go-diff is a Go implementation of Google's Diff, Match, and Patch library
+// Original library is Copyright (c) 2006 Google Inc.
+// http://code.google.com/p/google-diff-match-patch/
+
+package diffmatchpatch
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "html"
+ "math"
+ "net/url"
+ "regexp"
+ "strconv"
+ "strings"
+ "time"
+ "unicode/utf8"
+)
+
+// Operation defines the operation of a diff item.
+type Operation int8
+
+//go:generate stringer -type=Operation -trimprefix=Diff
+
+const (
+ // DiffDelete item represents a delete diff.
+ DiffDelete Operation = -1
+ // DiffInsert item represents an insert diff.
+ DiffInsert Operation = 1
+ // DiffEqual item represents an equal diff.
+ DiffEqual Operation = 0
+)
+
+// Diff represents one diff operation
+type Diff struct {
+ Type Operation
+ Text string
+}
+
+// splice removes amount elements from slice at index index, replacing them with elements.
+func splice(slice []Diff, index int, amount int, elements ...Diff) []Diff {
+ if len(elements) == amount {
+ // Easy case: overwrite the relevant items.
+ copy(slice[index:], elements)
+ return slice
+ }
+ if len(elements) < amount {
+ // Fewer new items than old.
+ // Copy in the new items.
+ copy(slice[index:], elements)
+ // Shift the remaining items left.
+ copy(slice[index+len(elements):], slice[index+amount:])
+ // Calculate the new end of the slice.
+ end := len(slice) - amount + len(elements)
+ // Zero stranded elements at end so that they can be garbage collected.
+ tail := slice[end:]
+ for i := range tail {
+ tail[i] = Diff{}
+ }
+ return slice[:end]
+ }
+ // More new items than old.
+ // Make room in slice for new elements.
+ // There's probably an even more efficient way to do this,
+ // but this is simple and clear.
+ need := len(slice) - amount + len(elements)
+ for len(slice) < need {
+ slice = append(slice, Diff{})
+ }
+ // Shift slice elements right to make room for new elements.
+ copy(slice[index+len(elements):], slice[index+amount:])
+ // Copy in new elements.
+ copy(slice[index:], elements)
+ return slice
+}
+
+// DiffMain finds the differences between two texts.
+// If an invalid UTF-8 sequence is encountered, it will be replaced by the Unicode replacement character.
+func (dmp *DiffMatchPatch) DiffMain(text1, text2 string, checklines bool) []Diff {
+ return dmp.DiffMainRunes([]rune(text1), []rune(text2), checklines)
+}
+
+// DiffMainRunes finds the differences between two rune sequences.
+// If an invalid UTF-8 sequence is encountered, it will be replaced by the Unicode replacement character.
+func (dmp *DiffMatchPatch) DiffMainRunes(text1, text2 []rune, checklines bool) []Diff {
+ var deadline time.Time
+ if dmp.DiffTimeout > 0 {
+ deadline = time.Now().Add(dmp.DiffTimeout)
+ }
+ return dmp.diffMainRunes(text1, text2, checklines, deadline)
+}
+
+func (dmp *DiffMatchPatch) diffMainRunes(text1, text2 []rune, checklines bool, deadline time.Time) []Diff {
+ if runesEqual(text1, text2) {
+ var diffs []Diff
+ if len(text1) > 0 {
+ diffs = append(diffs, Diff{DiffEqual, string(text1)})
+ }
+ return diffs
+ }
+ // Trim off common prefix (speedup).
+ commonlength := commonPrefixLength(text1, text2)
+ commonprefix := text1[:commonlength]
+ text1 = text1[commonlength:]
+ text2 = text2[commonlength:]
+
+ // Trim off common suffix (speedup).
+ commonlength = commonSuffixLength(text1, text2)
+ commonsuffix := text1[len(text1)-commonlength:]
+ text1 = text1[:len(text1)-commonlength]
+ text2 = text2[:len(text2)-commonlength]
+
+ // Compute the diff on the middle block.
+ diffs := dmp.diffCompute(text1, text2, checklines, deadline)
+
+ // Restore the prefix and suffix.
+ if len(commonprefix) != 0 {
+ diffs = append([]Diff{{DiffEqual, string(commonprefix)}}, diffs...)
+ }
+ if len(commonsuffix) != 0 {
+ diffs = append(diffs, Diff{DiffEqual, string(commonsuffix)})
+ }
+
+ return dmp.DiffCleanupMerge(diffs)
+}
+
+// diffCompute finds the differences between two rune slices. Assumes that the texts do not have any common prefix or suffix.
+func (dmp *DiffMatchPatch) diffCompute(text1, text2 []rune, checklines bool, deadline time.Time) []Diff {
+ diffs := []Diff{}
+ if len(text1) == 0 {
+ // Just add some text (speedup).
+ return append(diffs, Diff{DiffInsert, string(text2)})
+ } else if len(text2) == 0 {
+ // Just delete some text (speedup).
+ return append(diffs, Diff{DiffDelete, string(text1)})
+ }
+
+ var longtext, shorttext []rune
+ if len(text1) > len(text2) {
+ longtext = text1
+ shorttext = text2
+ } else {
+ longtext = text2
+ shorttext = text1
+ }
+
+ if i := runesIndex(longtext, shorttext); i != -1 {
+ op := DiffInsert
+ // Swap insertions for deletions if diff is reversed.
+ if len(text1) > len(text2) {
+ op = DiffDelete
+ }
+ // Shorter text is inside the longer text (speedup).
+ return []Diff{
+ Diff{op, string(longtext[:i])},
+ Diff{DiffEqual, string(shorttext)},
+ Diff{op, string(longtext[i+len(shorttext):])},
+ }
+ } else if len(shorttext) == 1 {
+ // Single character string.
+ // After the previous speedup, the character can't be an equality.
+ return []Diff{
+ {DiffDelete, string(text1)},
+ {DiffInsert, string(text2)},
+ }
+ // Check to see if the problem can be split in two.
+ } else if hm := dmp.diffHalfMatch(text1, text2); hm != nil {
+ // A half-match was found, sort out the return data.
+ text1A := hm[0]
+ text1B := hm[1]
+ text2A := hm[2]
+ text2B := hm[3]
+ midCommon := hm[4]
+ // Send both pairs off for separate processing.
+ diffsA := dmp.diffMainRunes(text1A, text2A, checklines, deadline)
+ diffsB := dmp.diffMainRunes(text1B, text2B, checklines, deadline)
+ // Merge the results.
+ diffs := diffsA
+ diffs = append(diffs, Diff{DiffEqual, string(midCommon)})
+ diffs = append(diffs, diffsB...)
+ return diffs
+ } else if checklines && len(text1) > 100 && len(text2) > 100 {
+ return dmp.diffLineMode(text1, text2, deadline)
+ }
+ return dmp.diffBisect(text1, text2, deadline)
+}
+
+// diffLineMode does a quick line-level diff on both []runes, then rediff the parts for greater accuracy. This speedup can produce non-minimal diffs.
+func (dmp *DiffMatchPatch) diffLineMode(text1, text2 []rune, deadline time.Time) []Diff {
+ // Scan the text on a line-by-line basis first.
+ text1, text2, linearray := dmp.DiffLinesToRunes(string(text1), string(text2))
+
+ diffs := dmp.diffMainRunes(text1, text2, false, deadline)
+
+ // Convert the diff back to original text.
+ diffs = dmp.DiffCharsToLines(diffs, linearray)
+ // Eliminate freak matches (e.g. blank lines)
+ diffs = dmp.DiffCleanupSemantic(diffs)
+
+ // Rediff any replacement blocks, this time character-by-character.
+ // Add a dummy entry at the end.
+ diffs = append(diffs, Diff{DiffEqual, ""})
+
+ pointer := 0
+ countDelete := 0
+ countInsert := 0
+
+ // NOTE: Rune slices are slower than using strings in this case.
+ textDelete := ""
+ textInsert := ""
+
+ for pointer < len(diffs) {
+ switch diffs[pointer].Type {
+ case DiffInsert:
+ countInsert++
+ textInsert += diffs[pointer].Text
+ case DiffDelete:
+ countDelete++
+ textDelete += diffs[pointer].Text
+ case DiffEqual:
+ // Upon reaching an equality, check for prior redundancies.
+ if countDelete >= 1 && countInsert >= 1 {
+ // Delete the offending records and add the merged ones.
+ diffs = splice(diffs, pointer-countDelete-countInsert,
+ countDelete+countInsert)
+
+ pointer = pointer - countDelete - countInsert
+ a := dmp.diffMainRunes([]rune(textDelete), []rune(textInsert), false, deadline)
+ for j := len(a) - 1; j >= 0; j-- {
+ diffs = splice(diffs, pointer, 0, a[j])
+ }
+ pointer = pointer + len(a)
+ }
+
+ countInsert = 0
+ countDelete = 0
+ textDelete = ""
+ textInsert = ""
+ }
+ pointer++
+ }
+
+ return diffs[:len(diffs)-1] // Remove the dummy entry at the end.
+}
+
+// DiffBisect finds the 'middle snake' of a diff, split the problem in two and return the recursively constructed diff.
+// If an invalid UTF-8 sequence is encountered, it will be replaced by the Unicode replacement character.
+// See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations.
+func (dmp *DiffMatchPatch) DiffBisect(text1, text2 string, deadline time.Time) []Diff {
+ // Unused in this code, but retained for interface compatibility.
+ return dmp.diffBisect([]rune(text1), []rune(text2), deadline)
+}
+
+// diffBisect finds the 'middle snake' of a diff, splits the problem in two and returns the recursively constructed diff.
+// See Myers's 1986 paper: An O(ND) Difference Algorithm and Its Variations.
+func (dmp *DiffMatchPatch) diffBisect(runes1, runes2 []rune, deadline time.Time) []Diff {
+ // Cache the text lengths to prevent multiple calls.
+ runes1Len, runes2Len := len(runes1), len(runes2)
+
+ maxD := (runes1Len + runes2Len + 1) / 2
+ vOffset := maxD
+ vLength := 2 * maxD
+
+ v1 := make([]int, vLength)
+ v2 := make([]int, vLength)
+ for i := range v1 {
+ v1[i] = -1
+ v2[i] = -1
+ }
+ v1[vOffset+1] = 0
+ v2[vOffset+1] = 0
+
+ delta := runes1Len - runes2Len
+ // If the total number of characters is odd, then the front path will collide with the reverse path.
+ front := (delta%2 != 0)
+ // Offsets for start and end of k loop. Prevents mapping of space beyond the grid.
+ k1start := 0
+ k1end := 0
+ k2start := 0
+ k2end := 0
+ for d := 0; d < maxD; d++ {
+ // Bail out if deadline is reached.
+ if !deadline.IsZero() && d%16 == 0 && time.Now().After(deadline) {
+ break
+ }
+
+ // Walk the front path one step.
+ for k1 := -d + k1start; k1 <= d-k1end; k1 += 2 {
+ k1Offset := vOffset + k1
+ var x1 int
+
+ if k1 == -d || (k1 != d && v1[k1Offset-1] < v1[k1Offset+1]) {
+ x1 = v1[k1Offset+1]
+ } else {
+ x1 = v1[k1Offset-1] + 1
+ }
+
+ y1 := x1 - k1
+ for x1 < runes1Len && y1 < runes2Len {
+ if runes1[x1] != runes2[y1] {
+ break
+ }
+ x1++
+ y1++
+ }
+ v1[k1Offset] = x1
+ if x1 > runes1Len {
+ // Ran off the right of the graph.
+ k1end += 2
+ } else if y1 > runes2Len {
+ // Ran off the bottom of the graph.
+ k1start += 2
+ } else if front {
+ k2Offset := vOffset + delta - k1
+ if k2Offset >= 0 && k2Offset < vLength && v2[k2Offset] != -1 {
+ // Mirror x2 onto top-left coordinate system.
+ x2 := runes1Len - v2[k2Offset]
+ if x1 >= x2 {
+ // Overlap detected.
+ return dmp.diffBisectSplit(runes1, runes2, x1, y1, deadline)
+ }
+ }
+ }
+ }
+ // Walk the reverse path one step.
+ for k2 := -d + k2start; k2 <= d-k2end; k2 += 2 {
+ k2Offset := vOffset + k2
+ var x2 int
+ if k2 == -d || (k2 != d && v2[k2Offset-1] < v2[k2Offset+1]) {
+ x2 = v2[k2Offset+1]
+ } else {
+ x2 = v2[k2Offset-1] + 1
+ }
+ var y2 = x2 - k2
+ for x2 < runes1Len && y2 < runes2Len {
+ if runes1[runes1Len-x2-1] != runes2[runes2Len-y2-1] {
+ break
+ }
+ x2++
+ y2++
+ }
+ v2[k2Offset] = x2
+ if x2 > runes1Len {
+ // Ran off the left of the graph.
+ k2end += 2
+ } else if y2 > runes2Len {
+ // Ran off the top of the graph.
+ k2start += 2
+ } else if !front {
+ k1Offset := vOffset + delta - k2
+ if k1Offset >= 0 && k1Offset < vLength && v1[k1Offset] != -1 {
+ x1 := v1[k1Offset]
+ y1 := vOffset + x1 - k1Offset
+ // Mirror x2 onto top-left coordinate system.
+ x2 = runes1Len - x2
+ if x1 >= x2 {
+ // Overlap detected.
+ return dmp.diffBisectSplit(runes1, runes2, x1, y1, deadline)
+ }
+ }
+ }
+ }
+ }
+ // Diff took too long and hit the deadline or number of diffs equals number of characters, no commonality at all.
+ return []Diff{
+ {DiffDelete, string(runes1)},
+ {DiffInsert, string(runes2)},
+ }
+}
+
+func (dmp *DiffMatchPatch) diffBisectSplit(runes1, runes2 []rune, x, y int,
+ deadline time.Time) []Diff {
+ runes1a := runes1[:x]
+ runes2a := runes2[:y]
+ runes1b := runes1[x:]
+ runes2b := runes2[y:]
+
+ // Compute both diffs serially.
+ diffs := dmp.diffMainRunes(runes1a, runes2a, false, deadline)
+ diffsb := dmp.diffMainRunes(runes1b, runes2b, false, deadline)
+
+ return append(diffs, diffsb...)
+}
+
+// DiffLinesToChars splits two texts into a list of strings, and educes the texts to a string of hashes where each Unicode character represents one line.
+// It's slightly faster to call DiffLinesToRunes first, followed by DiffMainRunes.
+func (dmp *DiffMatchPatch) DiffLinesToChars(text1, text2 string) (string, string, []string) {
+ chars1, chars2, lineArray := dmp.diffLinesToStrings(text1, text2)
+ return chars1, chars2, lineArray
+}
+
+// DiffLinesToRunes splits two texts into a list of runes.
+func (dmp *DiffMatchPatch) DiffLinesToRunes(text1, text2 string) ([]rune, []rune, []string) {
+ chars1, chars2, lineArray := dmp.diffLinesToStrings(text1, text2)
+ return []rune(chars1), []rune(chars2), lineArray
+}
+
+// DiffCharsToLines rehydrates the text in a diff from a string of line hashes to real lines of text.
+func (dmp *DiffMatchPatch) DiffCharsToLines(diffs []Diff, lineArray []string) []Diff {
+ hydrated := make([]Diff, 0, len(diffs))
+ for _, aDiff := range diffs {
+ runes := []rune(aDiff.Text)
+ text := make([]string, len(runes))
+
+ for i, r := range runes {
+ text[i] = lineArray[runeToInt(r)]
+ }
+
+ aDiff.Text = strings.Join(text, "")
+ hydrated = append(hydrated, aDiff)
+ }
+ return hydrated
+}
+
+// DiffCommonPrefix determines the common prefix length of two strings.
+func (dmp *DiffMatchPatch) DiffCommonPrefix(text1, text2 string) int {
+ // Unused in this code, but retained for interface compatibility.
+ return commonPrefixLength([]rune(text1), []rune(text2))
+}
+
+// DiffCommonSuffix determines the common suffix length of two strings.
+func (dmp *DiffMatchPatch) DiffCommonSuffix(text1, text2 string) int {
+ // Unused in this code, but retained for interface compatibility.
+ return commonSuffixLength([]rune(text1), []rune(text2))
+}
+
+// commonPrefixLength returns the length of the common prefix of two rune slices.
+func commonPrefixLength(text1, text2 []rune) int {
+ // Linear search. See comment in commonSuffixLength.
+ n := 0
+ for ; n < len(text1) && n < len(text2); n++ {
+ if text1[n] != text2[n] {
+ return n
+ }
+ }
+ return n
+}
+
+// commonSuffixLength returns the length of the common suffix of two rune slices.
+func commonSuffixLength(text1, text2 []rune) int {
+ // Use linear search rather than the binary search discussed at https://neil.fraser.name/news/2007/10/09/.
+ // See discussion at https://github.com/sergi/go-diff/issues/54.
+ i1 := len(text1)
+ i2 := len(text2)
+ for n := 0; ; n++ {
+ i1--
+ i2--
+ if i1 < 0 || i2 < 0 || text1[i1] != text2[i2] {
+ return n
+ }
+ }
+}
+
+// DiffCommonOverlap determines if the suffix of one string is the prefix of another.
+func (dmp *DiffMatchPatch) DiffCommonOverlap(text1 string, text2 string) int {
+ // Cache the text lengths to prevent multiple calls.
+ text1Length := len(text1)
+ text2Length := len(text2)
+ // Eliminate the null case.
+ if text1Length == 0 || text2Length == 0 {
+ return 0
+ }
+ // Truncate the longer string.
+ if text1Length > text2Length {
+ text1 = text1[text1Length-text2Length:]
+ } else if text1Length < text2Length {
+ text2 = text2[0:text1Length]
+ }
+ textLength := int(math.Min(float64(text1Length), float64(text2Length)))
+ // Quick check for the worst case.
+ if text1 == text2 {
+ return textLength
+ }
+
+ // Start by looking for a single character match and increase length until no match is found. Performance analysis: http://neil.fraser.name/news/2010/11/04/
+ best := 0
+ length := 1
+ for {
+ pattern := text1[textLength-length:]
+ found := strings.Index(text2, pattern)
+ if found == -1 {
+ break
+ }
+ length += found
+ if found == 0 || text1[textLength-length:] == text2[0:length] {
+ best = length
+ length++
+ }
+ }
+
+ return best
+}
+
+// DiffHalfMatch checks whether the two texts share a substring which is at least half the length of the longer text. This speedup can produce non-minimal diffs.
+func (dmp *DiffMatchPatch) DiffHalfMatch(text1, text2 string) []string {
+ // Unused in this code, but retained for interface compatibility.
+ runeSlices := dmp.diffHalfMatch([]rune(text1), []rune(text2))
+ if runeSlices == nil {
+ return nil
+ }
+
+ result := make([]string, len(runeSlices))
+ for i, r := range runeSlices {
+ result[i] = string(r)
+ }
+ return result
+}
+
+func (dmp *DiffMatchPatch) diffHalfMatch(text1, text2 []rune) [][]rune {
+ if dmp.DiffTimeout <= 0 {
+ // Don't risk returning a non-optimal diff if we have unlimited time.
+ return nil
+ }
+
+ var longtext, shorttext []rune
+ if len(text1) > len(text2) {
+ longtext = text1
+ shorttext = text2
+ } else {
+ longtext = text2
+ shorttext = text1
+ }
+
+ if len(longtext) < 4 || len(shorttext)*2 < len(longtext) {
+ return nil // Pointless.
+ }
+
+ // First check if the second quarter is the seed for a half-match.
+ hm1 := dmp.diffHalfMatchI(longtext, shorttext, int(float64(len(longtext)+3)/4))
+
+ // Check again based on the third quarter.
+ hm2 := dmp.diffHalfMatchI(longtext, shorttext, int(float64(len(longtext)+1)/2))
+
+ hm := [][]rune{}
+ if hm1 == nil && hm2 == nil {
+ return nil
+ } else if hm2 == nil {
+ hm = hm1
+ } else if hm1 == nil {
+ hm = hm2
+ } else {
+ // Both matched. Select the longest.
+ if len(hm1[4]) > len(hm2[4]) {
+ hm = hm1
+ } else {
+ hm = hm2
+ }
+ }
+
+ // A half-match was found, sort out the return data.
+ if len(text1) > len(text2) {
+ return hm
+ }
+
+ return [][]rune{hm[2], hm[3], hm[0], hm[1], hm[4]}
+}
+
+// diffHalfMatchI checks if a substring of shorttext exist within longtext such that the substring is at least half the length of longtext?
+// Returns a slice containing the prefix of longtext, the suffix of longtext, the prefix of shorttext, the suffix of shorttext and the common middle, or null if there was no match.
+func (dmp *DiffMatchPatch) diffHalfMatchI(l, s []rune, i int) [][]rune {
+ var bestCommonA []rune
+ var bestCommonB []rune
+ var bestCommonLen int
+ var bestLongtextA []rune
+ var bestLongtextB []rune
+ var bestShorttextA []rune
+ var bestShorttextB []rune
+
+ // Start with a 1/4 length substring at position i as a seed.
+ seed := l[i : i+len(l)/4]
+
+ for j := runesIndexOf(s, seed, 0); j != -1; j = runesIndexOf(s, seed, j+1) {
+ prefixLength := commonPrefixLength(l[i:], s[j:])
+ suffixLength := commonSuffixLength(l[:i], s[:j])
+
+ if bestCommonLen < suffixLength+prefixLength {
+ bestCommonA = s[j-suffixLength : j]
+ bestCommonB = s[j : j+prefixLength]
+ bestCommonLen = len(bestCommonA) + len(bestCommonB)
+ bestLongtextA = l[:i-suffixLength]
+ bestLongtextB = l[i+prefixLength:]
+ bestShorttextA = s[:j-suffixLength]
+ bestShorttextB = s[j+prefixLength:]
+ }
+ }
+
+ if bestCommonLen*2 < len(l) {
+ return nil
+ }
+
+ return [][]rune{
+ bestLongtextA,
+ bestLongtextB,
+ bestShorttextA,
+ bestShorttextB,
+ append(bestCommonA, bestCommonB...),
+ }
+}
+
+// DiffCleanupSemantic reduces the number of edits by eliminating semantically trivial equalities.
+func (dmp *DiffMatchPatch) DiffCleanupSemantic(diffs []Diff) []Diff {
+ changes := false
+ // Stack of indices where equalities are found.
+ equalities := make([]int, 0, len(diffs))
+
+ var lastequality string
+ // Always equal to diffs[equalities[equalitiesLength - 1]][1]
+ var pointer int // Index of current position.
+ // Number of characters that changed prior to the equality.
+ var lengthInsertions1, lengthDeletions1 int
+ // Number of characters that changed after the equality.
+ var lengthInsertions2, lengthDeletions2 int
+
+ for pointer < len(diffs) {
+ if diffs[pointer].Type == DiffEqual {
+ // Equality found.
+ equalities = append(equalities, pointer)
+ lengthInsertions1 = lengthInsertions2
+ lengthDeletions1 = lengthDeletions2
+ lengthInsertions2 = 0
+ lengthDeletions2 = 0
+ lastequality = diffs[pointer].Text
+ } else {
+ // An insertion or deletion.
+
+ if diffs[pointer].Type == DiffInsert {
+ lengthInsertions2 += utf8.RuneCountInString(diffs[pointer].Text)
+ } else {
+ lengthDeletions2 += utf8.RuneCountInString(diffs[pointer].Text)
+ }
+ // Eliminate an equality that is smaller or equal to the edits on both sides of it.
+ difference1 := int(math.Max(float64(lengthInsertions1), float64(lengthDeletions1)))
+ difference2 := int(math.Max(float64(lengthInsertions2), float64(lengthDeletions2)))
+ if utf8.RuneCountInString(lastequality) > 0 &&
+ (utf8.RuneCountInString(lastequality) <= difference1) &&
+ (utf8.RuneCountInString(lastequality) <= difference2) {
+ // Duplicate record.
+ insPoint := equalities[len(equalities)-1]
+ diffs = splice(diffs, insPoint, 0, Diff{DiffDelete, lastequality})
+
+ // Change second copy to insert.
+ diffs[insPoint+1].Type = DiffInsert
+ // Throw away the equality we just deleted.
+ equalities = equalities[:len(equalities)-1]
+
+ if len(equalities) > 0 {
+ equalities = equalities[:len(equalities)-1]
+ }
+ pointer = -1
+ if len(equalities) > 0 {
+ pointer = equalities[len(equalities)-1]
+ }
+
+ lengthInsertions1 = 0 // Reset the counters.
+ lengthDeletions1 = 0
+ lengthInsertions2 = 0
+ lengthDeletions2 = 0
+ lastequality = ""
+ changes = true
+ }
+ }
+ pointer++
+ }
+
+ // Normalize the diff.
+ if changes {
+ diffs = dmp.DiffCleanupMerge(diffs)
+ }
+ diffs = dmp.DiffCleanupSemanticLossless(diffs)
+ // Find any overlaps between deletions and insertions.
+ // e.g: abcxxxxxxdef
+ // -> abcxxxdef
+ // e.g: xxxabcdefxxx
+ // -> defxxxabc
+ // Only extract an overlap if it is as big as the edit ahead or behind it.
+ pointer = 1
+ for pointer < len(diffs) {
+ if diffs[pointer-1].Type == DiffDelete &&
+ diffs[pointer].Type == DiffInsert {
+ deletion := diffs[pointer-1].Text
+ insertion := diffs[pointer].Text
+ overlapLength1 := dmp.DiffCommonOverlap(deletion, insertion)
+ overlapLength2 := dmp.DiffCommonOverlap(insertion, deletion)
+ if overlapLength1 >= overlapLength2 {
+ if float64(overlapLength1) >= float64(utf8.RuneCountInString(deletion))/2 ||
+ float64(overlapLength1) >= float64(utf8.RuneCountInString(insertion))/2 {
+
+ // Overlap found. Insert an equality and trim the surrounding edits.
+ diffs = splice(diffs, pointer, 0, Diff{DiffEqual, insertion[:overlapLength1]})
+ diffs[pointer-1].Text =
+ deletion[0 : len(deletion)-overlapLength1]
+ diffs[pointer+1].Text = insertion[overlapLength1:]
+ pointer++
+ }
+ } else {
+ if float64(overlapLength2) >= float64(utf8.RuneCountInString(deletion))/2 ||
+ float64(overlapLength2) >= float64(utf8.RuneCountInString(insertion))/2 {
+ // Reverse overlap found. Insert an equality and swap and trim the surrounding edits.
+ overlap := Diff{DiffEqual, deletion[:overlapLength2]}
+ diffs = splice(diffs, pointer, 0, overlap)
+ diffs[pointer-1].Type = DiffInsert
+ diffs[pointer-1].Text = insertion[0 : len(insertion)-overlapLength2]
+ diffs[pointer+1].Type = DiffDelete
+ diffs[pointer+1].Text = deletion[overlapLength2:]
+ pointer++
+ }
+ }
+ pointer++
+ }
+ pointer++
+ }
+
+ return diffs
+}
+
+// Define some regex patterns for matching boundaries.
+var (
+ nonAlphaNumericRegex = regexp.MustCompile(`[^a-zA-Z0-9]`)
+ whitespaceRegex = regexp.MustCompile(`\s`)
+ linebreakRegex = regexp.MustCompile(`[\r\n]`)
+ blanklineEndRegex = regexp.MustCompile(`\n\r?\n$`)
+ blanklineStartRegex = regexp.MustCompile(`^\r?\n\r?\n`)
+)
+
+// diffCleanupSemanticScore computes a score representing whether the internal boundary falls on logical boundaries.
+// Scores range from 6 (best) to 0 (worst). Closure, but does not reference any external variables.
+func diffCleanupSemanticScore(one, two string) int {
+ if len(one) == 0 || len(two) == 0 {
+ // Edges are the best.
+ return 6
+ }
+
+ // Each port of this function behaves slightly differently due to subtle differences in each language's definition of things like 'whitespace'. Since this function's purpose is largely cosmetic, the choice has been made to use each language's native features rather than force total conformity.
+ rune1, _ := utf8.DecodeLastRuneInString(one)
+ rune2, _ := utf8.DecodeRuneInString(two)
+ char1 := string(rune1)
+ char2 := string(rune2)
+
+ nonAlphaNumeric1 := nonAlphaNumericRegex.MatchString(char1)
+ nonAlphaNumeric2 := nonAlphaNumericRegex.MatchString(char2)
+ whitespace1 := nonAlphaNumeric1 && whitespaceRegex.MatchString(char1)
+ whitespace2 := nonAlphaNumeric2 && whitespaceRegex.MatchString(char2)
+ lineBreak1 := whitespace1 && linebreakRegex.MatchString(char1)
+ lineBreak2 := whitespace2 && linebreakRegex.MatchString(char2)
+ blankLine1 := lineBreak1 && blanklineEndRegex.MatchString(one)
+ blankLine2 := lineBreak2 && blanklineEndRegex.MatchString(two)
+
+ if blankLine1 || blankLine2 {
+ // Five points for blank lines.
+ return 5
+ } else if lineBreak1 || lineBreak2 {
+ // Four points for line breaks.
+ return 4
+ } else if nonAlphaNumeric1 && !whitespace1 && whitespace2 {
+ // Three points for end of sentences.
+ return 3
+ } else if whitespace1 || whitespace2 {
+ // Two points for whitespace.
+ return 2
+ } else if nonAlphaNumeric1 || nonAlphaNumeric2 {
+ // One point for non-alphanumeric.
+ return 1
+ }
+ return 0
+}
+
+// DiffCleanupSemanticLossless looks for single edits surrounded on both sides by equalities which can be shifted sideways to align the edit to a word boundary.
+// E.g: The cat came. -> The cat came.
+func (dmp *DiffMatchPatch) DiffCleanupSemanticLossless(diffs []Diff) []Diff {
+ pointer := 1
+
+ // Intentionally ignore the first and last element (don't need checking).
+ for pointer < len(diffs)-1 {
+ if diffs[pointer-1].Type == DiffEqual &&
+ diffs[pointer+1].Type == DiffEqual {
+
+ // This is a single edit surrounded by equalities.
+ equality1 := diffs[pointer-1].Text
+ edit := diffs[pointer].Text
+ equality2 := diffs[pointer+1].Text
+
+ // First, shift the edit as far left as possible.
+ commonOffset := dmp.DiffCommonSuffix(equality1, edit)
+ if commonOffset > 0 {
+ commonString := edit[len(edit)-commonOffset:]
+ equality1 = equality1[0 : len(equality1)-commonOffset]
+ edit = commonString + edit[:len(edit)-commonOffset]
+ equality2 = commonString + equality2
+ }
+
+ // Second, step character by character right, looking for the best fit.
+ bestEquality1 := equality1
+ bestEdit := edit
+ bestEquality2 := equality2
+ bestScore := diffCleanupSemanticScore(equality1, edit) +
+ diffCleanupSemanticScore(edit, equality2)
+
+ for len(edit) != 0 && len(equality2) != 0 {
+ _, sz := utf8.DecodeRuneInString(edit)
+ if len(equality2) < sz || edit[:sz] != equality2[:sz] {
+ break
+ }
+ equality1 += edit[:sz]
+ edit = edit[sz:] + equality2[:sz]
+ equality2 = equality2[sz:]
+ score := diffCleanupSemanticScore(equality1, edit) +
+ diffCleanupSemanticScore(edit, equality2)
+ // The >= encourages trailing rather than leading whitespace on edits.
+ if score >= bestScore {
+ bestScore = score
+ bestEquality1 = equality1
+ bestEdit = edit
+ bestEquality2 = equality2
+ }
+ }
+
+ if diffs[pointer-1].Text != bestEquality1 {
+ // We have an improvement, save it back to the diff.
+ if len(bestEquality1) != 0 {
+ diffs[pointer-1].Text = bestEquality1
+ } else {
+ diffs = splice(diffs, pointer-1, 1)
+ pointer--
+ }
+
+ diffs[pointer].Text = bestEdit
+ if len(bestEquality2) != 0 {
+ diffs[pointer+1].Text = bestEquality2
+ } else {
+ diffs = append(diffs[:pointer+1], diffs[pointer+2:]...)
+ pointer--
+ }
+ }
+ }
+ pointer++
+ }
+
+ return diffs
+}
+
+// DiffCleanupEfficiency reduces the number of edits by eliminating operationally trivial equalities.
+func (dmp *DiffMatchPatch) DiffCleanupEfficiency(diffs []Diff) []Diff {
+ changes := false
+ // Stack of indices where equalities are found.
+ type equality struct {
+ data int
+ next *equality
+ }
+ var equalities *equality
+ // Always equal to equalities[equalitiesLength-1][1]
+ lastequality := ""
+ pointer := 0 // Index of current position.
+ // Is there an insertion operation before the last equality.
+ preIns := false
+ // Is there a deletion operation before the last equality.
+ preDel := false
+ // Is there an insertion operation after the last equality.
+ postIns := false
+ // Is there a deletion operation after the last equality.
+ postDel := false
+ for pointer < len(diffs) {
+ if diffs[pointer].Type == DiffEqual { // Equality found.
+ if len(diffs[pointer].Text) < dmp.DiffEditCost &&
+ (postIns || postDel) {
+ // Candidate found.
+ equalities = &equality{
+ data: pointer,
+ next: equalities,
+ }
+ preIns = postIns
+ preDel = postDel
+ lastequality = diffs[pointer].Text
+ } else {
+ // Not a candidate, and can never become one.
+ equalities = nil
+ lastequality = ""
+ }
+ postIns = false
+ postDel = false
+ } else { // An insertion or deletion.
+ if diffs[pointer].Type == DiffDelete {
+ postDel = true
+ } else {
+ postIns = true
+ }
+
+ // Five types to be split:
+ // ABXYCD
+ // AXCD
+ // ABXC
+ // AXCD
+ // ABXC
+ var sumPres int
+ if preIns {
+ sumPres++
+ }
+ if preDel {
+ sumPres++
+ }
+ if postIns {
+ sumPres++
+ }
+ if postDel {
+ sumPres++
+ }
+ if len(lastequality) > 0 &&
+ ((preIns && preDel && postIns && postDel) ||
+ ((len(lastequality) < dmp.DiffEditCost/2) && sumPres == 3)) {
+
+ insPoint := equalities.data
+
+ // Duplicate record.
+ diffs = splice(diffs, insPoint, 0, Diff{DiffDelete, lastequality})
+
+ // Change second copy to insert.
+ diffs[insPoint+1].Type = DiffInsert
+ // Throw away the equality we just deleted.
+ equalities = equalities.next
+ lastequality = ""
+
+ if preIns && preDel {
+ // No changes made which could affect previous entry, keep going.
+ postIns = true
+ postDel = true
+ equalities = nil
+ } else {
+ if equalities != nil {
+ equalities = equalities.next
+ }
+ if equalities != nil {
+ pointer = equalities.data
+ } else {
+ pointer = -1
+ }
+ postIns = false
+ postDel = false
+ }
+ changes = true
+ }
+ }
+ pointer++
+ }
+
+ if changes {
+ diffs = dmp.DiffCleanupMerge(diffs)
+ }
+
+ return diffs
+}
+
+// DiffCleanupMerge reorders and merges like edit sections. Merge equalities.
+// Any edit section can move as long as it doesn't cross an equality.
+func (dmp *DiffMatchPatch) DiffCleanupMerge(diffs []Diff) []Diff {
+ // Add a dummy entry at the end.
+ diffs = append(diffs, Diff{DiffEqual, ""})
+ pointer := 0
+ countDelete := 0
+ countInsert := 0
+ commonlength := 0
+ textDelete := []rune(nil)
+ textInsert := []rune(nil)
+
+ for pointer < len(diffs) {
+ switch diffs[pointer].Type {
+ case DiffInsert:
+ countInsert++
+ textInsert = append(textInsert, []rune(diffs[pointer].Text)...)
+ pointer++
+ break
+ case DiffDelete:
+ countDelete++
+ textDelete = append(textDelete, []rune(diffs[pointer].Text)...)
+ pointer++
+ break
+ case DiffEqual:
+ // Upon reaching an equality, check for prior redundancies.
+ if countDelete+countInsert > 1 {
+ if countDelete != 0 && countInsert != 0 {
+ // Factor out any common prefixies.
+ commonlength = commonPrefixLength(textInsert, textDelete)
+ if commonlength != 0 {
+ x := pointer - countDelete - countInsert
+ if x > 0 && diffs[x-1].Type == DiffEqual {
+ diffs[x-1].Text += string(textInsert[:commonlength])
+ } else {
+ diffs = append([]Diff{{DiffEqual, string(textInsert[:commonlength])}}, diffs...)
+ pointer++
+ }
+ textInsert = textInsert[commonlength:]
+ textDelete = textDelete[commonlength:]
+ }
+ // Factor out any common suffixies.
+ commonlength = commonSuffixLength(textInsert, textDelete)
+ if commonlength != 0 {
+ insertIndex := len(textInsert) - commonlength
+ deleteIndex := len(textDelete) - commonlength
+ diffs[pointer].Text = string(textInsert[insertIndex:]) + diffs[pointer].Text
+ textInsert = textInsert[:insertIndex]
+ textDelete = textDelete[:deleteIndex]
+ }
+ }
+ // Delete the offending records and add the merged ones.
+ if countDelete == 0 {
+ diffs = splice(diffs, pointer-countInsert,
+ countDelete+countInsert,
+ Diff{DiffInsert, string(textInsert)})
+ } else if countInsert == 0 {
+ diffs = splice(diffs, pointer-countDelete,
+ countDelete+countInsert,
+ Diff{DiffDelete, string(textDelete)})
+ } else {
+ diffs = splice(diffs, pointer-countDelete-countInsert,
+ countDelete+countInsert,
+ Diff{DiffDelete, string(textDelete)},
+ Diff{DiffInsert, string(textInsert)})
+ }
+
+ pointer = pointer - countDelete - countInsert + 1
+ if countDelete != 0 {
+ pointer++
+ }
+ if countInsert != 0 {
+ pointer++
+ }
+ } else if pointer != 0 && diffs[pointer-1].Type == DiffEqual {
+ // Merge this equality with the previous one.
+ diffs[pointer-1].Text += diffs[pointer].Text
+ diffs = append(diffs[:pointer], diffs[pointer+1:]...)
+ } else {
+ pointer++
+ }
+ countInsert = 0
+ countDelete = 0
+ textDelete = nil
+ textInsert = nil
+ break
+ }
+ }
+
+ if len(diffs[len(diffs)-1].Text) == 0 {
+ diffs = diffs[0 : len(diffs)-1] // Remove the dummy entry at the end.
+ }
+
+ // Second pass: look for single edits surrounded on both sides by equalities which can be shifted sideways to eliminate an equality. E.g: ABAC -> ABAC
+ changes := false
+ pointer = 1
+ // Intentionally ignore the first and last element (don't need checking).
+ for pointer < (len(diffs) - 1) {
+ if diffs[pointer-1].Type == DiffEqual &&
+ diffs[pointer+1].Type == DiffEqual {
+ // This is a single edit surrounded by equalities.
+ if strings.HasSuffix(diffs[pointer].Text, diffs[pointer-1].Text) {
+ // Shift the edit over the previous equality.
+ diffs[pointer].Text = diffs[pointer-1].Text +
+ diffs[pointer].Text[:len(diffs[pointer].Text)-len(diffs[pointer-1].Text)]
+ diffs[pointer+1].Text = diffs[pointer-1].Text + diffs[pointer+1].Text
+ diffs = splice(diffs, pointer-1, 1)
+ changes = true
+ } else if strings.HasPrefix(diffs[pointer].Text, diffs[pointer+1].Text) {
+ // Shift the edit over the next equality.
+ diffs[pointer-1].Text += diffs[pointer+1].Text
+ diffs[pointer].Text =
+ diffs[pointer].Text[len(diffs[pointer+1].Text):] + diffs[pointer+1].Text
+ diffs = splice(diffs, pointer+1, 1)
+ changes = true
+ }
+ }
+ pointer++
+ }
+
+ // If shifts were made, the diff needs reordering and another shift sweep.
+ if changes {
+ diffs = dmp.DiffCleanupMerge(diffs)
+ }
+
+ return diffs
+}
+
+// DiffXIndex returns the equivalent location in s2.
+func (dmp *DiffMatchPatch) DiffXIndex(diffs []Diff, loc int) int {
+ chars1 := 0
+ chars2 := 0
+ lastChars1 := 0
+ lastChars2 := 0
+ lastDiff := Diff{}
+ for i := 0; i < len(diffs); i++ {
+ aDiff := diffs[i]
+ if aDiff.Type != DiffInsert {
+ // Equality or deletion.
+ chars1 += len(aDiff.Text)
+ }
+ if aDiff.Type != DiffDelete {
+ // Equality or insertion.
+ chars2 += len(aDiff.Text)
+ }
+ if chars1 > loc {
+ // Overshot the location.
+ lastDiff = aDiff
+ break
+ }
+ lastChars1 = chars1
+ lastChars2 = chars2
+ }
+ if lastDiff.Type == DiffDelete {
+ // The location was deleted.
+ return lastChars2
+ }
+ // Add the remaining character length.
+ return lastChars2 + (loc - lastChars1)
+}
+
+// DiffPrettyHtml converts a []Diff into a pretty HTML report.
+// It is intended as an example from which to write one's own display functions.
+func (dmp *DiffMatchPatch) DiffPrettyHtml(diffs []Diff) string {
+ var buff bytes.Buffer
+ for _, diff := range diffs {
+ text := strings.Replace(html.EscapeString(diff.Text), "\n", "¶
", -1)
+ switch diff.Type {
+ case DiffInsert:
+ _, _ = buff.WriteString("")
+ _, _ = buff.WriteString(text)
+ _, _ = buff.WriteString("")
+ case DiffDelete:
+ _, _ = buff.WriteString("")
+ _, _ = buff.WriteString(text)
+ _, _ = buff.WriteString("")
+ case DiffEqual:
+ _, _ = buff.WriteString("")
+ _, _ = buff.WriteString(text)
+ _, _ = buff.WriteString("")
+ }
+ }
+ return buff.String()
+}
+
+// DiffPrettyText converts a []Diff into a colored text report.
+func (dmp *DiffMatchPatch) DiffPrettyText(diffs []Diff) string {
+ var buff bytes.Buffer
+ for _, diff := range diffs {
+ text := diff.Text
+
+ switch diff.Type {
+ case DiffInsert:
+ _, _ = buff.WriteString("\x1b[32m")
+ _, _ = buff.WriteString(text)
+ _, _ = buff.WriteString("\x1b[0m")
+ case DiffDelete:
+ _, _ = buff.WriteString("\x1b[31m")
+ _, _ = buff.WriteString(text)
+ _, _ = buff.WriteString("\x1b[0m")
+ case DiffEqual:
+ _, _ = buff.WriteString(text)
+ }
+ }
+
+ return buff.String()
+}
+
+// DiffText1 computes and returns the source text (all equalities and deletions).
+func (dmp *DiffMatchPatch) DiffText1(diffs []Diff) string {
+ //StringBuilder text = new StringBuilder()
+ var text bytes.Buffer
+
+ for _, aDiff := range diffs {
+ if aDiff.Type != DiffInsert {
+ _, _ = text.WriteString(aDiff.Text)
+ }
+ }
+ return text.String()
+}
+
+// DiffText2 computes and returns the destination text (all equalities and insertions).
+func (dmp *DiffMatchPatch) DiffText2(diffs []Diff) string {
+ var text bytes.Buffer
+
+ for _, aDiff := range diffs {
+ if aDiff.Type != DiffDelete {
+ _, _ = text.WriteString(aDiff.Text)
+ }
+ }
+ return text.String()
+}
+
+// DiffLevenshtein computes the Levenshtein distance that is the number of inserted, deleted or substituted characters.
+func (dmp *DiffMatchPatch) DiffLevenshtein(diffs []Diff) int {
+ levenshtein := 0
+ insertions := 0
+ deletions := 0
+
+ for _, aDiff := range diffs {
+ switch aDiff.Type {
+ case DiffInsert:
+ insertions += utf8.RuneCountInString(aDiff.Text)
+ case DiffDelete:
+ deletions += utf8.RuneCountInString(aDiff.Text)
+ case DiffEqual:
+ // A deletion and an insertion is one substitution.
+ levenshtein += max(insertions, deletions)
+ insertions = 0
+ deletions = 0
+ }
+ }
+
+ levenshtein += max(insertions, deletions)
+ return levenshtein
+}
+
+// DiffToDelta crushes the diff into an encoded string which describes the operations required to transform text1 into text2.
+// E.g. =3\t-2\t+ing -> Keep 3 chars, delete 2 chars, insert 'ing'. Operations are tab-separated. Inserted text is escaped using %xx notation.
+func (dmp *DiffMatchPatch) DiffToDelta(diffs []Diff) string {
+ var text bytes.Buffer
+ for _, aDiff := range diffs {
+ switch aDiff.Type {
+ case DiffInsert:
+ _, _ = text.WriteString("+")
+ _, _ = text.WriteString(strings.Replace(url.QueryEscape(aDiff.Text), "+", " ", -1))
+ _, _ = text.WriteString("\t")
+ break
+ case DiffDelete:
+ _, _ = text.WriteString("-")
+ _, _ = text.WriteString(strconv.Itoa(utf8.RuneCountInString(aDiff.Text)))
+ _, _ = text.WriteString("\t")
+ break
+ case DiffEqual:
+ _, _ = text.WriteString("=")
+ _, _ = text.WriteString(strconv.Itoa(utf8.RuneCountInString(aDiff.Text)))
+ _, _ = text.WriteString("\t")
+ break
+ }
+ }
+ delta := text.String()
+ if len(delta) != 0 {
+ // Strip off trailing tab character.
+ delta = delta[0 : utf8.RuneCountInString(delta)-1]
+ delta = unescaper.Replace(delta)
+ }
+ return delta
+}
+
+// DiffFromDelta given the original text1, and an encoded string which describes the operations required to transform text1 into text2, comAdde the full diff.
+func (dmp *DiffMatchPatch) DiffFromDelta(text1 string, delta string) (diffs []Diff, err error) {
+ i := 0
+ runes := []rune(text1)
+
+ for _, token := range strings.Split(delta, "\t") {
+ if len(token) == 0 {
+ // Blank tokens are ok (from a trailing \t).
+ continue
+ }
+
+ // Each token begins with a one character parameter which specifies the operation of this token (delete, insert, equality).
+ param := token[1:]
+
+ switch op := token[0]; op {
+ case '+':
+ // Decode would Diff all "+" to " "
+ param = strings.Replace(param, "+", "%2b", -1)
+ param, err = url.QueryUnescape(param)
+ if err != nil {
+ return nil, err
+ }
+ if !utf8.ValidString(param) {
+ return nil, fmt.Errorf("invalid UTF-8 token: %q", param)
+ }
+
+ diffs = append(diffs, Diff{DiffInsert, param})
+ case '=', '-':
+ n, err := strconv.ParseInt(param, 10, 0)
+ if err != nil {
+ return nil, err
+ } else if n < 0 {
+ return nil, errors.New("Negative number in DiffFromDelta: " + param)
+ }
+
+ i += int(n)
+ // Break out if we are out of bounds, go1.6 can't handle this very well
+ if i > len(runes) {
+ break
+ }
+ // Remember that string slicing is by byte - we want by rune here.
+ text := string(runes[i-int(n) : i])
+
+ if op == '=' {
+ diffs = append(diffs, Diff{DiffEqual, text})
+ } else {
+ diffs = append(diffs, Diff{DiffDelete, text})
+ }
+ default:
+ // Anything else is an error.
+ return nil, errors.New("Invalid diff operation in DiffFromDelta: " + string(token[0]))
+ }
+ }
+
+ if i != len(runes) {
+ return nil, fmt.Errorf("Delta length (%v) is different from source text length (%v)", i, len(text1))
+ }
+
+ return diffs, nil
+}
+
+// diffLinesToStrings splits two texts into a list of strings. Each string represents one line.
+func (dmp *DiffMatchPatch) diffLinesToStrings(text1, text2 string) (string, string, []string) {
+ // '\x00' is a valid character, but various debuggers don't like it. So we'll insert a junk entry to avoid generating a null character.
+ lineArray := []string{""} // e.g. lineArray[4] == 'Hello\n'
+
+ lineHash := make(map[string]int)
+ //Each string has the index of lineArray which it points to
+ strIndexArray1 := dmp.diffLinesToStringsMunge(text1, &lineArray, lineHash)
+ strIndexArray2 := dmp.diffLinesToStringsMunge(text2, &lineArray, lineHash)
+
+ return intArrayToString(strIndexArray1), intArrayToString(strIndexArray2), lineArray
+}
+
+// diffLinesToStringsMunge splits a text into an array of strings, and reduces the texts to a []string.
+func (dmp *DiffMatchPatch) diffLinesToStringsMunge(text string, lineArray *[]string, lineHash map[string]int) []uint32 {
+ // Walk the text, pulling out a substring for each line. text.split('\n') would would temporarily double our memory footprint. Modifying text would create many large strings to garbage collect.
+ lineStart := 0
+ lineEnd := -1
+ strs := []uint32{}
+
+ for lineEnd < len(text)-1 {
+ lineEnd = indexOf(text, "\n", lineStart)
+
+ if lineEnd == -1 {
+ lineEnd = len(text) - 1
+ }
+
+ line := text[lineStart : lineEnd+1]
+ lineStart = lineEnd + 1
+ lineValue, ok := lineHash[line]
+
+ if ok {
+ strs = append(strs, uint32(lineValue))
+ } else {
+ *lineArray = append(*lineArray, line)
+ lineHash[line] = len(*lineArray) - 1
+ strs = append(strs, uint32(len(*lineArray)-1))
+ }
+ }
+
+ return strs
+}
diff --git a/vendor/github.com/sergi/go-diff/diffmatchpatch/diffmatchpatch.go b/vendor/github.com/sergi/go-diff/diffmatchpatch/diffmatchpatch.go
new file mode 100644
index 00000000..d3acc32c
--- /dev/null
+++ b/vendor/github.com/sergi/go-diff/diffmatchpatch/diffmatchpatch.go
@@ -0,0 +1,46 @@
+// Copyright (c) 2012-2016 The go-diff authors. All rights reserved.
+// https://github.com/sergi/go-diff
+// See the included LICENSE file for license details.
+//
+// go-diff is a Go implementation of Google's Diff, Match, and Patch library
+// Original library is Copyright (c) 2006 Google Inc.
+// http://code.google.com/p/google-diff-match-patch/
+
+// Package diffmatchpatch offers robust algorithms to perform the operations required for synchronizing plain text.
+package diffmatchpatch
+
+import (
+ "time"
+)
+
+// DiffMatchPatch holds the configuration for diff-match-patch operations.
+type DiffMatchPatch struct {
+ // Number of seconds to map a diff before giving up (0 for infinity).
+ DiffTimeout time.Duration
+ // Cost of an empty edit operation in terms of edit characters.
+ DiffEditCost int
+ // How far to search for a match (0 = exact location, 1000+ = broad match). A match this many characters away from the expected location will add 1.0 to the score (0.0 is a perfect match).
+ MatchDistance int
+ // When deleting a large block of text (over ~64 characters), how close do the contents have to be to match the expected contents. (0.0 = perfection, 1.0 = very loose). Note that MatchThreshold controls how closely the end points of a delete need to match.
+ PatchDeleteThreshold float64
+ // Chunk size for context length.
+ PatchMargin int
+ // The number of bits in an int.
+ MatchMaxBits int
+ // At what point is no match declared (0.0 = perfection, 1.0 = very loose).
+ MatchThreshold float64
+}
+
+// New creates a new DiffMatchPatch object with default parameters.
+func New() *DiffMatchPatch {
+ // Defaults.
+ return &DiffMatchPatch{
+ DiffTimeout: time.Second,
+ DiffEditCost: 4,
+ MatchThreshold: 0.5,
+ MatchDistance: 1000,
+ PatchDeleteThreshold: 0.5,
+ PatchMargin: 4,
+ MatchMaxBits: 32,
+ }
+}
diff --git a/vendor/github.com/sergi/go-diff/diffmatchpatch/match.go b/vendor/github.com/sergi/go-diff/diffmatchpatch/match.go
new file mode 100644
index 00000000..17374e10
--- /dev/null
+++ b/vendor/github.com/sergi/go-diff/diffmatchpatch/match.go
@@ -0,0 +1,160 @@
+// Copyright (c) 2012-2016 The go-diff authors. All rights reserved.
+// https://github.com/sergi/go-diff
+// See the included LICENSE file for license details.
+//
+// go-diff is a Go implementation of Google's Diff, Match, and Patch library
+// Original library is Copyright (c) 2006 Google Inc.
+// http://code.google.com/p/google-diff-match-patch/
+
+package diffmatchpatch
+
+import (
+ "math"
+)
+
+// MatchMain locates the best instance of 'pattern' in 'text' near 'loc'.
+// Returns -1 if no match found.
+func (dmp *DiffMatchPatch) MatchMain(text, pattern string, loc int) int {
+ // Check for null inputs not needed since null can't be passed in C#.
+
+ loc = int(math.Max(0, math.Min(float64(loc), float64(len(text)))))
+ if text == pattern {
+ // Shortcut (potentially not guaranteed by the algorithm)
+ return 0
+ } else if len(text) == 0 {
+ // Nothing to match.
+ return -1
+ } else if loc+len(pattern) <= len(text) && text[loc:loc+len(pattern)] == pattern {
+ // Perfect match at the perfect spot! (Includes case of null pattern)
+ return loc
+ }
+ // Do a fuzzy compare.
+ return dmp.MatchBitap(text, pattern, loc)
+}
+
+// MatchBitap locates the best instance of 'pattern' in 'text' near 'loc' using the Bitap algorithm.
+// Returns -1 if no match was found.
+func (dmp *DiffMatchPatch) MatchBitap(text, pattern string, loc int) int {
+ // Initialise the alphabet.
+ s := dmp.MatchAlphabet(pattern)
+
+ // Highest score beyond which we give up.
+ scoreThreshold := dmp.MatchThreshold
+ // Is there a nearby exact match? (speedup)
+ bestLoc := indexOf(text, pattern, loc)
+ if bestLoc != -1 {
+ scoreThreshold = math.Min(dmp.matchBitapScore(0, bestLoc, loc,
+ pattern), scoreThreshold)
+ // What about in the other direction? (speedup)
+ bestLoc = lastIndexOf(text, pattern, loc+len(pattern))
+ if bestLoc != -1 {
+ scoreThreshold = math.Min(dmp.matchBitapScore(0, bestLoc, loc,
+ pattern), scoreThreshold)
+ }
+ }
+
+ // Initialise the bit arrays.
+ matchmask := 1 << uint((len(pattern) - 1))
+ bestLoc = -1
+
+ var binMin, binMid int
+ binMax := len(pattern) + len(text)
+ lastRd := []int{}
+ for d := 0; d < len(pattern); d++ {
+ // Scan for the best match; each iteration allows for one more error. Run a binary search to determine how far from 'loc' we can stray at this error level.
+ binMin = 0
+ binMid = binMax
+ for binMin < binMid {
+ if dmp.matchBitapScore(d, loc+binMid, loc, pattern) <= scoreThreshold {
+ binMin = binMid
+ } else {
+ binMax = binMid
+ }
+ binMid = (binMax-binMin)/2 + binMin
+ }
+ // Use the result from this iteration as the maximum for the next.
+ binMax = binMid
+ start := int(math.Max(1, float64(loc-binMid+1)))
+ finish := int(math.Min(float64(loc+binMid), float64(len(text))) + float64(len(pattern)))
+
+ rd := make([]int, finish+2)
+ rd[finish+1] = (1 << uint(d)) - 1
+
+ for j := finish; j >= start; j-- {
+ var charMatch int
+ if len(text) <= j-1 {
+ // Out of range.
+ charMatch = 0
+ } else if _, ok := s[text[j-1]]; !ok {
+ charMatch = 0
+ } else {
+ charMatch = s[text[j-1]]
+ }
+
+ if d == 0 {
+ // First pass: exact match.
+ rd[j] = ((rd[j+1] << 1) | 1) & charMatch
+ } else {
+ // Subsequent passes: fuzzy match.
+ rd[j] = ((rd[j+1]<<1)|1)&charMatch | (((lastRd[j+1] | lastRd[j]) << 1) | 1) | lastRd[j+1]
+ }
+ if (rd[j] & matchmask) != 0 {
+ score := dmp.matchBitapScore(d, j-1, loc, pattern)
+ // This match will almost certainly be better than any existing match. But check anyway.
+ if score <= scoreThreshold {
+ // Told you so.
+ scoreThreshold = score
+ bestLoc = j - 1
+ if bestLoc > loc {
+ // When passing loc, don't exceed our current distance from loc.
+ start = int(math.Max(1, float64(2*loc-bestLoc)))
+ } else {
+ // Already passed loc, downhill from here on in.
+ break
+ }
+ }
+ }
+ }
+ if dmp.matchBitapScore(d+1, loc, loc, pattern) > scoreThreshold {
+ // No hope for a (better) match at greater error levels.
+ break
+ }
+ lastRd = rd
+ }
+ return bestLoc
+}
+
+// matchBitapScore computes and returns the score for a match with e errors and x location.
+func (dmp *DiffMatchPatch) matchBitapScore(e, x, loc int, pattern string) float64 {
+ accuracy := float64(e) / float64(len(pattern))
+ proximity := math.Abs(float64(loc - x))
+ if dmp.MatchDistance == 0 {
+ // Dodge divide by zero error.
+ if proximity == 0 {
+ return accuracy
+ }
+
+ return 1.0
+ }
+ return accuracy + (proximity / float64(dmp.MatchDistance))
+}
+
+// MatchAlphabet initialises the alphabet for the Bitap algorithm.
+func (dmp *DiffMatchPatch) MatchAlphabet(pattern string) map[byte]int {
+ s := map[byte]int{}
+ charPattern := []byte(pattern)
+ for _, c := range charPattern {
+ _, ok := s[c]
+ if !ok {
+ s[c] = 0
+ }
+ }
+ i := 0
+
+ for _, c := range charPattern {
+ value := s[c] | int(uint(1)< y {
+ return x
+ }
+ return y
+}
diff --git a/vendor/github.com/sergi/go-diff/diffmatchpatch/operation_string.go b/vendor/github.com/sergi/go-diff/diffmatchpatch/operation_string.go
new file mode 100644
index 00000000..533ec0da
--- /dev/null
+++ b/vendor/github.com/sergi/go-diff/diffmatchpatch/operation_string.go
@@ -0,0 +1,17 @@
+// Code generated by "stringer -type=Operation -trimprefix=Diff"; DO NOT EDIT.
+
+package diffmatchpatch
+
+import "fmt"
+
+const _Operation_name = "DeleteEqualInsert"
+
+var _Operation_index = [...]uint8{0, 6, 11, 17}
+
+func (i Operation) String() string {
+ i -= -1
+ if i < 0 || i >= Operation(len(_Operation_index)-1) {
+ return fmt.Sprintf("Operation(%d)", i+-1)
+ }
+ return _Operation_name[_Operation_index[i]:_Operation_index[i+1]]
+}
diff --git a/vendor/github.com/sergi/go-diff/diffmatchpatch/patch.go b/vendor/github.com/sergi/go-diff/diffmatchpatch/patch.go
new file mode 100644
index 00000000..0dbe3bdd
--- /dev/null
+++ b/vendor/github.com/sergi/go-diff/diffmatchpatch/patch.go
@@ -0,0 +1,556 @@
+// Copyright (c) 2012-2016 The go-diff authors. All rights reserved.
+// https://github.com/sergi/go-diff
+// See the included LICENSE file for license details.
+//
+// go-diff is a Go implementation of Google's Diff, Match, and Patch library
+// Original library is Copyright (c) 2006 Google Inc.
+// http://code.google.com/p/google-diff-match-patch/
+
+package diffmatchpatch
+
+import (
+ "bytes"
+ "errors"
+ "math"
+ "net/url"
+ "regexp"
+ "strconv"
+ "strings"
+)
+
+// Patch represents one patch operation.
+type Patch struct {
+ diffs []Diff
+ Start1 int
+ Start2 int
+ Length1 int
+ Length2 int
+}
+
+// String emulates GNU diff's format.
+// Header: @@ -382,8 +481,9 @@
+// Indices are printed as 1-based, not 0-based.
+func (p *Patch) String() string {
+ var coords1, coords2 string
+
+ if p.Length1 == 0 {
+ coords1 = strconv.Itoa(p.Start1) + ",0"
+ } else if p.Length1 == 1 {
+ coords1 = strconv.Itoa(p.Start1 + 1)
+ } else {
+ coords1 = strconv.Itoa(p.Start1+1) + "," + strconv.Itoa(p.Length1)
+ }
+
+ if p.Length2 == 0 {
+ coords2 = strconv.Itoa(p.Start2) + ",0"
+ } else if p.Length2 == 1 {
+ coords2 = strconv.Itoa(p.Start2 + 1)
+ } else {
+ coords2 = strconv.Itoa(p.Start2+1) + "," + strconv.Itoa(p.Length2)
+ }
+
+ var text bytes.Buffer
+ _, _ = text.WriteString("@@ -" + coords1 + " +" + coords2 + " @@\n")
+
+ // Escape the body of the patch with %xx notation.
+ for _, aDiff := range p.diffs {
+ switch aDiff.Type {
+ case DiffInsert:
+ _, _ = text.WriteString("+")
+ case DiffDelete:
+ _, _ = text.WriteString("-")
+ case DiffEqual:
+ _, _ = text.WriteString(" ")
+ }
+
+ _, _ = text.WriteString(strings.Replace(url.QueryEscape(aDiff.Text), "+", " ", -1))
+ _, _ = text.WriteString("\n")
+ }
+
+ return unescaper.Replace(text.String())
+}
+
+// PatchAddContext increases the context until it is unique, but doesn't let the pattern expand beyond MatchMaxBits.
+func (dmp *DiffMatchPatch) PatchAddContext(patch Patch, text string) Patch {
+ if len(text) == 0 {
+ return patch
+ }
+
+ pattern := text[patch.Start2 : patch.Start2+patch.Length1]
+ padding := 0
+
+ // Look for the first and last matches of pattern in text. If two different matches are found, increase the pattern length.
+ for strings.Index(text, pattern) != strings.LastIndex(text, pattern) &&
+ len(pattern) < dmp.MatchMaxBits-2*dmp.PatchMargin {
+ padding += dmp.PatchMargin
+ maxStart := max(0, patch.Start2-padding)
+ minEnd := min(len(text), patch.Start2+patch.Length1+padding)
+ pattern = text[maxStart:minEnd]
+ }
+ // Add one chunk for good luck.
+ padding += dmp.PatchMargin
+
+ // Add the prefix.
+ prefix := text[max(0, patch.Start2-padding):patch.Start2]
+ if len(prefix) != 0 {
+ patch.diffs = append([]Diff{Diff{DiffEqual, prefix}}, patch.diffs...)
+ }
+ // Add the suffix.
+ suffix := text[patch.Start2+patch.Length1 : min(len(text), patch.Start2+patch.Length1+padding)]
+ if len(suffix) != 0 {
+ patch.diffs = append(patch.diffs, Diff{DiffEqual, suffix})
+ }
+
+ // Roll back the start points.
+ patch.Start1 -= len(prefix)
+ patch.Start2 -= len(prefix)
+ // Extend the lengths.
+ patch.Length1 += len(prefix) + len(suffix)
+ patch.Length2 += len(prefix) + len(suffix)
+
+ return patch
+}
+
+// PatchMake computes a list of patches.
+func (dmp *DiffMatchPatch) PatchMake(opt ...interface{}) []Patch {
+ if len(opt) == 1 {
+ diffs, _ := opt[0].([]Diff)
+ text1 := dmp.DiffText1(diffs)
+ return dmp.PatchMake(text1, diffs)
+ } else if len(opt) == 2 {
+ text1 := opt[0].(string)
+ switch t := opt[1].(type) {
+ case string:
+ diffs := dmp.DiffMain(text1, t, true)
+ if len(diffs) > 2 {
+ diffs = dmp.DiffCleanupSemantic(diffs)
+ diffs = dmp.DiffCleanupEfficiency(diffs)
+ }
+ return dmp.PatchMake(text1, diffs)
+ case []Diff:
+ return dmp.patchMake2(text1, t)
+ }
+ } else if len(opt) == 3 {
+ return dmp.PatchMake(opt[0], opt[2])
+ }
+ return []Patch{}
+}
+
+// patchMake2 computes a list of patches to turn text1 into text2.
+// text2 is not provided, diffs are the delta between text1 and text2.
+func (dmp *DiffMatchPatch) patchMake2(text1 string, diffs []Diff) []Patch {
+ // Check for null inputs not needed since null can't be passed in C#.
+ patches := []Patch{}
+ if len(diffs) == 0 {
+ return patches // Get rid of the null case.
+ }
+
+ patch := Patch{}
+ charCount1 := 0 // Number of characters into the text1 string.
+ charCount2 := 0 // Number of characters into the text2 string.
+ // Start with text1 (prepatchText) and apply the diffs until we arrive at text2 (postpatchText). We recreate the patches one by one to determine context info.
+ prepatchText := text1
+ postpatchText := text1
+
+ for i, aDiff := range diffs {
+ if len(patch.diffs) == 0 && aDiff.Type != DiffEqual {
+ // A new patch starts here.
+ patch.Start1 = charCount1
+ patch.Start2 = charCount2
+ }
+
+ switch aDiff.Type {
+ case DiffInsert:
+ patch.diffs = append(patch.diffs, aDiff)
+ patch.Length2 += len(aDiff.Text)
+ postpatchText = postpatchText[:charCount2] +
+ aDiff.Text + postpatchText[charCount2:]
+ case DiffDelete:
+ patch.Length1 += len(aDiff.Text)
+ patch.diffs = append(patch.diffs, aDiff)
+ postpatchText = postpatchText[:charCount2] + postpatchText[charCount2+len(aDiff.Text):]
+ case DiffEqual:
+ if len(aDiff.Text) <= 2*dmp.PatchMargin &&
+ len(patch.diffs) != 0 && i != len(diffs)-1 {
+ // Small equality inside a patch.
+ patch.diffs = append(patch.diffs, aDiff)
+ patch.Length1 += len(aDiff.Text)
+ patch.Length2 += len(aDiff.Text)
+ }
+ if len(aDiff.Text) >= 2*dmp.PatchMargin {
+ // Time for a new patch.
+ if len(patch.diffs) != 0 {
+ patch = dmp.PatchAddContext(patch, prepatchText)
+ patches = append(patches, patch)
+ patch = Patch{}
+ // Unlike Unidiff, our patch lists have a rolling context. http://code.google.com/p/google-diff-match-patch/wiki/Unidiff Update prepatch text & pos to reflect the application of the just completed patch.
+ prepatchText = postpatchText
+ charCount1 = charCount2
+ }
+ }
+ }
+
+ // Update the current character count.
+ if aDiff.Type != DiffInsert {
+ charCount1 += len(aDiff.Text)
+ }
+ if aDiff.Type != DiffDelete {
+ charCount2 += len(aDiff.Text)
+ }
+ }
+
+ // Pick up the leftover patch if not empty.
+ if len(patch.diffs) != 0 {
+ patch = dmp.PatchAddContext(patch, prepatchText)
+ patches = append(patches, patch)
+ }
+
+ return patches
+}
+
+// PatchDeepCopy returns an array that is identical to a given an array of patches.
+func (dmp *DiffMatchPatch) PatchDeepCopy(patches []Patch) []Patch {
+ patchesCopy := []Patch{}
+ for _, aPatch := range patches {
+ patchCopy := Patch{}
+ for _, aDiff := range aPatch.diffs {
+ patchCopy.diffs = append(patchCopy.diffs, Diff{
+ aDiff.Type,
+ aDiff.Text,
+ })
+ }
+ patchCopy.Start1 = aPatch.Start1
+ patchCopy.Start2 = aPatch.Start2
+ patchCopy.Length1 = aPatch.Length1
+ patchCopy.Length2 = aPatch.Length2
+ patchesCopy = append(patchesCopy, patchCopy)
+ }
+ return patchesCopy
+}
+
+// PatchApply merges a set of patches onto the text. Returns a patched text, as well as an array of true/false values indicating which patches were applied.
+func (dmp *DiffMatchPatch) PatchApply(patches []Patch, text string) (string, []bool) {
+ if len(patches) == 0 {
+ return text, []bool{}
+ }
+
+ // Deep copy the patches so that no changes are made to originals.
+ patches = dmp.PatchDeepCopy(patches)
+
+ nullPadding := dmp.PatchAddPadding(patches)
+ text = nullPadding + text + nullPadding
+ patches = dmp.PatchSplitMax(patches)
+
+ x := 0
+ // delta keeps track of the offset between the expected and actual location of the previous patch. If there are patches expected at positions 10 and 20, but the first patch was found at 12, delta is 2 and the second patch has an effective expected position of 22.
+ delta := 0
+ results := make([]bool, len(patches))
+ for _, aPatch := range patches {
+ expectedLoc := aPatch.Start2 + delta
+ text1 := dmp.DiffText1(aPatch.diffs)
+ var startLoc int
+ endLoc := -1
+ if len(text1) > dmp.MatchMaxBits {
+ // PatchSplitMax will only provide an oversized pattern in the case of a monster delete.
+ startLoc = dmp.MatchMain(text, text1[:dmp.MatchMaxBits], expectedLoc)
+ if startLoc != -1 {
+ endLoc = dmp.MatchMain(text,
+ text1[len(text1)-dmp.MatchMaxBits:], expectedLoc+len(text1)-dmp.MatchMaxBits)
+ if endLoc == -1 || startLoc >= endLoc {
+ // Can't find valid trailing context. Drop this patch.
+ startLoc = -1
+ }
+ }
+ } else {
+ startLoc = dmp.MatchMain(text, text1, expectedLoc)
+ }
+ if startLoc == -1 {
+ // No match found. :(
+ results[x] = false
+ // Subtract the delta for this failed patch from subsequent patches.
+ delta -= aPatch.Length2 - aPatch.Length1
+ } else {
+ // Found a match. :)
+ results[x] = true
+ delta = startLoc - expectedLoc
+ var text2 string
+ if endLoc == -1 {
+ text2 = text[startLoc:int(math.Min(float64(startLoc+len(text1)), float64(len(text))))]
+ } else {
+ text2 = text[startLoc:int(math.Min(float64(endLoc+dmp.MatchMaxBits), float64(len(text))))]
+ }
+ if text1 == text2 {
+ // Perfect match, just shove the Replacement text in.
+ text = text[:startLoc] + dmp.DiffText2(aPatch.diffs) + text[startLoc+len(text1):]
+ } else {
+ // Imperfect match. Run a diff to get a framework of equivalent indices.
+ diffs := dmp.DiffMain(text1, text2, false)
+ if len(text1) > dmp.MatchMaxBits && float64(dmp.DiffLevenshtein(diffs))/float64(len(text1)) > dmp.PatchDeleteThreshold {
+ // The end points match, but the content is unacceptably bad.
+ results[x] = false
+ } else {
+ diffs = dmp.DiffCleanupSemanticLossless(diffs)
+ index1 := 0
+ for _, aDiff := range aPatch.diffs {
+ if aDiff.Type != DiffEqual {
+ index2 := dmp.DiffXIndex(diffs, index1)
+ if aDiff.Type == DiffInsert {
+ // Insertion
+ text = text[:startLoc+index2] + aDiff.Text + text[startLoc+index2:]
+ } else if aDiff.Type == DiffDelete {
+ // Deletion
+ startIndex := startLoc + index2
+ text = text[:startIndex] +
+ text[startIndex+dmp.DiffXIndex(diffs, index1+len(aDiff.Text))-index2:]
+ }
+ }
+ if aDiff.Type != DiffDelete {
+ index1 += len(aDiff.Text)
+ }
+ }
+ }
+ }
+ }
+ x++
+ }
+ // Strip the padding off.
+ text = text[len(nullPadding) : len(nullPadding)+(len(text)-2*len(nullPadding))]
+ return text, results
+}
+
+// PatchAddPadding adds some padding on text start and end so that edges can match something.
+// Intended to be called only from within patchApply.
+func (dmp *DiffMatchPatch) PatchAddPadding(patches []Patch) string {
+ paddingLength := dmp.PatchMargin
+ nullPadding := ""
+ for x := 1; x <= paddingLength; x++ {
+ nullPadding += string(rune(x))
+ }
+
+ // Bump all the patches forward.
+ for i := range patches {
+ patches[i].Start1 += paddingLength
+ patches[i].Start2 += paddingLength
+ }
+
+ // Add some padding on start of first diff.
+ if len(patches[0].diffs) == 0 || patches[0].diffs[0].Type != DiffEqual {
+ // Add nullPadding equality.
+ patches[0].diffs = append([]Diff{Diff{DiffEqual, nullPadding}}, patches[0].diffs...)
+ patches[0].Start1 -= paddingLength // Should be 0.
+ patches[0].Start2 -= paddingLength // Should be 0.
+ patches[0].Length1 += paddingLength
+ patches[0].Length2 += paddingLength
+ } else if paddingLength > len(patches[0].diffs[0].Text) {
+ // Grow first equality.
+ extraLength := paddingLength - len(patches[0].diffs[0].Text)
+ patches[0].diffs[0].Text = nullPadding[len(patches[0].diffs[0].Text):] + patches[0].diffs[0].Text
+ patches[0].Start1 -= extraLength
+ patches[0].Start2 -= extraLength
+ patches[0].Length1 += extraLength
+ patches[0].Length2 += extraLength
+ }
+
+ // Add some padding on end of last diff.
+ last := len(patches) - 1
+ if len(patches[last].diffs) == 0 || patches[last].diffs[len(patches[last].diffs)-1].Type != DiffEqual {
+ // Add nullPadding equality.
+ patches[last].diffs = append(patches[last].diffs, Diff{DiffEqual, nullPadding})
+ patches[last].Length1 += paddingLength
+ patches[last].Length2 += paddingLength
+ } else if paddingLength > len(patches[last].diffs[len(patches[last].diffs)-1].Text) {
+ // Grow last equality.
+ lastDiff := patches[last].diffs[len(patches[last].diffs)-1]
+ extraLength := paddingLength - len(lastDiff.Text)
+ patches[last].diffs[len(patches[last].diffs)-1].Text += nullPadding[:extraLength]
+ patches[last].Length1 += extraLength
+ patches[last].Length2 += extraLength
+ }
+
+ return nullPadding
+}
+
+// PatchSplitMax looks through the patches and breaks up any which are longer than the maximum limit of the match algorithm.
+// Intended to be called only from within patchApply.
+func (dmp *DiffMatchPatch) PatchSplitMax(patches []Patch) []Patch {
+ patchSize := dmp.MatchMaxBits
+ for x := 0; x < len(patches); x++ {
+ if patches[x].Length1 <= patchSize {
+ continue
+ }
+ bigpatch := patches[x]
+ // Remove the big old patch.
+ patches = append(patches[:x], patches[x+1:]...)
+ x--
+
+ Start1 := bigpatch.Start1
+ Start2 := bigpatch.Start2
+ precontext := ""
+ for len(bigpatch.diffs) != 0 {
+ // Create one of several smaller patches.
+ patch := Patch{}
+ empty := true
+ patch.Start1 = Start1 - len(precontext)
+ patch.Start2 = Start2 - len(precontext)
+ if len(precontext) != 0 {
+ patch.Length1 = len(precontext)
+ patch.Length2 = len(precontext)
+ patch.diffs = append(patch.diffs, Diff{DiffEqual, precontext})
+ }
+ for len(bigpatch.diffs) != 0 && patch.Length1 < patchSize-dmp.PatchMargin {
+ diffType := bigpatch.diffs[0].Type
+ diffText := bigpatch.diffs[0].Text
+ if diffType == DiffInsert {
+ // Insertions are harmless.
+ patch.Length2 += len(diffText)
+ Start2 += len(diffText)
+ patch.diffs = append(patch.diffs, bigpatch.diffs[0])
+ bigpatch.diffs = bigpatch.diffs[1:]
+ empty = false
+ } else if diffType == DiffDelete && len(patch.diffs) == 1 && patch.diffs[0].Type == DiffEqual && len(diffText) > 2*patchSize {
+ // This is a large deletion. Let it pass in one chunk.
+ patch.Length1 += len(diffText)
+ Start1 += len(diffText)
+ empty = false
+ patch.diffs = append(patch.diffs, Diff{diffType, diffText})
+ bigpatch.diffs = bigpatch.diffs[1:]
+ } else {
+ // Deletion or equality. Only take as much as we can stomach.
+ diffText = diffText[:min(len(diffText), patchSize-patch.Length1-dmp.PatchMargin)]
+
+ patch.Length1 += len(diffText)
+ Start1 += len(diffText)
+ if diffType == DiffEqual {
+ patch.Length2 += len(diffText)
+ Start2 += len(diffText)
+ } else {
+ empty = false
+ }
+ patch.diffs = append(patch.diffs, Diff{diffType, diffText})
+ if diffText == bigpatch.diffs[0].Text {
+ bigpatch.diffs = bigpatch.diffs[1:]
+ } else {
+ bigpatch.diffs[0].Text =
+ bigpatch.diffs[0].Text[len(diffText):]
+ }
+ }
+ }
+ // Compute the head context for the next patch.
+ precontext = dmp.DiffText2(patch.diffs)
+ precontext = precontext[max(0, len(precontext)-dmp.PatchMargin):]
+
+ postcontext := ""
+ // Append the end context for this patch.
+ if len(dmp.DiffText1(bigpatch.diffs)) > dmp.PatchMargin {
+ postcontext = dmp.DiffText1(bigpatch.diffs)[:dmp.PatchMargin]
+ } else {
+ postcontext = dmp.DiffText1(bigpatch.diffs)
+ }
+
+ if len(postcontext) != 0 {
+ patch.Length1 += len(postcontext)
+ patch.Length2 += len(postcontext)
+ if len(patch.diffs) != 0 && patch.diffs[len(patch.diffs)-1].Type == DiffEqual {
+ patch.diffs[len(patch.diffs)-1].Text += postcontext
+ } else {
+ patch.diffs = append(patch.diffs, Diff{DiffEqual, postcontext})
+ }
+ }
+ if !empty {
+ x++
+ patches = append(patches[:x], append([]Patch{patch}, patches[x:]...)...)
+ }
+ }
+ }
+ return patches
+}
+
+// PatchToText takes a list of patches and returns a textual representation.
+func (dmp *DiffMatchPatch) PatchToText(patches []Patch) string {
+ var text bytes.Buffer
+ for _, aPatch := range patches {
+ _, _ = text.WriteString(aPatch.String())
+ }
+ return text.String()
+}
+
+// PatchFromText parses a textual representation of patches and returns a List of Patch objects.
+func (dmp *DiffMatchPatch) PatchFromText(textline string) ([]Patch, error) {
+ patches := []Patch{}
+ if len(textline) == 0 {
+ return patches, nil
+ }
+ text := strings.Split(textline, "\n")
+ textPointer := 0
+ patchHeader := regexp.MustCompile("^@@ -(\\d+),?(\\d*) \\+(\\d+),?(\\d*) @@$")
+
+ var patch Patch
+ var sign uint8
+ var line string
+ for textPointer < len(text) {
+
+ if !patchHeader.MatchString(text[textPointer]) {
+ return patches, errors.New("Invalid patch string: " + text[textPointer])
+ }
+
+ patch = Patch{}
+ m := patchHeader.FindStringSubmatch(text[textPointer])
+
+ patch.Start1, _ = strconv.Atoi(m[1])
+ if len(m[2]) == 0 {
+ patch.Start1--
+ patch.Length1 = 1
+ } else if m[2] == "0" {
+ patch.Length1 = 0
+ } else {
+ patch.Start1--
+ patch.Length1, _ = strconv.Atoi(m[2])
+ }
+
+ patch.Start2, _ = strconv.Atoi(m[3])
+
+ if len(m[4]) == 0 {
+ patch.Start2--
+ patch.Length2 = 1
+ } else if m[4] == "0" {
+ patch.Length2 = 0
+ } else {
+ patch.Start2--
+ patch.Length2, _ = strconv.Atoi(m[4])
+ }
+ textPointer++
+
+ for textPointer < len(text) {
+ if len(text[textPointer]) > 0 {
+ sign = text[textPointer][0]
+ } else {
+ textPointer++
+ continue
+ }
+
+ line = text[textPointer][1:]
+ line = strings.Replace(line, "+", "%2b", -1)
+ line, _ = url.QueryUnescape(line)
+ if sign == '-' {
+ // Deletion.
+ patch.diffs = append(patch.diffs, Diff{DiffDelete, line})
+ } else if sign == '+' {
+ // Insertion.
+ patch.diffs = append(patch.diffs, Diff{DiffInsert, line})
+ } else if sign == ' ' {
+ // Minor equality.
+ patch.diffs = append(patch.diffs, Diff{DiffEqual, line})
+ } else if sign == '@' {
+ // Start of next patch.
+ break
+ } else {
+ // WTF?
+ return patches, errors.New("Invalid patch mode '" + string(sign) + "' in: " + string(line))
+ }
+ textPointer++
+ }
+
+ patches = append(patches, patch)
+ }
+ return patches, nil
+}
diff --git a/vendor/github.com/sergi/go-diff/diffmatchpatch/stringutil.go b/vendor/github.com/sergi/go-diff/diffmatchpatch/stringutil.go
new file mode 100644
index 00000000..eb727bb5
--- /dev/null
+++ b/vendor/github.com/sergi/go-diff/diffmatchpatch/stringutil.go
@@ -0,0 +1,190 @@
+// Copyright (c) 2012-2016 The go-diff authors. All rights reserved.
+// https://github.com/sergi/go-diff
+// See the included LICENSE file for license details.
+//
+// go-diff is a Go implementation of Google's Diff, Match, and Patch library
+// Original library is Copyright (c) 2006 Google Inc.
+// http://code.google.com/p/google-diff-match-patch/
+
+package diffmatchpatch
+
+import (
+ "fmt"
+ "strings"
+ "unicode/utf8"
+)
+
+const UNICODE_INVALID_RANGE_START = 0xD800
+const UNICODE_INVALID_RANGE_END = 0xDFFF
+const UNICODE_INVALID_RANGE_DELTA = UNICODE_INVALID_RANGE_END - UNICODE_INVALID_RANGE_START + 1
+const UNICODE_RANGE_MAX = 0x10FFFF
+
+// unescaper unescapes selected chars for compatibility with JavaScript's encodeURI.
+// In speed critical applications this could be dropped since the receiving application will certainly decode these fine. Note that this function is case-sensitive. Thus "%3F" would not be unescaped. But this is ok because it is only called with the output of HttpUtility.UrlEncode which returns lowercase hex. Example: "%3f" -> "?", "%24" -> "$", etc.
+var unescaper = strings.NewReplacer(
+ "%21", "!", "%7E", "~", "%27", "'",
+ "%28", "(", "%29", ")", "%3B", ";",
+ "%2F", "/", "%3F", "?", "%3A", ":",
+ "%40", "@", "%26", "&", "%3D", "=",
+ "%2B", "+", "%24", "$", "%2C", ",", "%23", "#", "%2A", "*")
+
+// indexOf returns the first index of pattern in str, starting at str[i].
+func indexOf(str string, pattern string, i int) int {
+ if i > len(str)-1 {
+ return -1
+ }
+ if i <= 0 {
+ return strings.Index(str, pattern)
+ }
+ ind := strings.Index(str[i:], pattern)
+ if ind == -1 {
+ return -1
+ }
+ return ind + i
+}
+
+// lastIndexOf returns the last index of pattern in str, starting at str[i].
+func lastIndexOf(str string, pattern string, i int) int {
+ if i < 0 {
+ return -1
+ }
+ if i >= len(str) {
+ return strings.LastIndex(str, pattern)
+ }
+ _, size := utf8.DecodeRuneInString(str[i:])
+ return strings.LastIndex(str[:i+size], pattern)
+}
+
+// runesIndexOf returns the index of pattern in target, starting at target[i].
+func runesIndexOf(target, pattern []rune, i int) int {
+ if i > len(target)-1 {
+ return -1
+ }
+ if i <= 0 {
+ return runesIndex(target, pattern)
+ }
+ ind := runesIndex(target[i:], pattern)
+ if ind == -1 {
+ return -1
+ }
+ return ind + i
+}
+
+func runesEqual(r1, r2 []rune) bool {
+ if len(r1) != len(r2) {
+ return false
+ }
+ for i, c := range r1 {
+ if c != r2[i] {
+ return false
+ }
+ }
+ return true
+}
+
+// runesIndex is the equivalent of strings.Index for rune slices.
+func runesIndex(r1, r2 []rune) int {
+ last := len(r1) - len(r2)
+ for i := 0; i <= last; i++ {
+ if runesEqual(r1[i:i+len(r2)], r2) {
+ return i
+ }
+ }
+ return -1
+}
+
+func intArrayToString(ns []uint32) string {
+ if len(ns) == 0 {
+ return ""
+ }
+
+ b := []rune{}
+ for _, n := range ns {
+ b = append(b, intToRune(n))
+ }
+ return string(b)
+}
+
+// These constants define the number of bits representable
+// in 1,2,3,4 byte utf8 sequences, respectively.
+const ONE_BYTE_BITS = 7
+const TWO_BYTE_BITS = 11
+const THREE_BYTE_BITS = 16
+const FOUR_BYTE_BITS = 21
+
+// Helper for getting a sequence of bits from an integer.
+func getBits(i uint32, cnt byte, from byte) byte {
+ return byte((i >> from) & ((1 << cnt) - 1))
+}
+
+// Converts an integer in the range 0~1112060 into a rune.
+// Based on the ranges table in https://en.wikipedia.org/wiki/UTF-8
+func intToRune(i uint32) rune {
+ if i < (1 << ONE_BYTE_BITS) {
+ return rune(i)
+ }
+
+ if i < (1 << TWO_BYTE_BITS) {
+ r, size := utf8.DecodeRune([]byte{0b11000000 | getBits(i, 5, 6), 0b10000000 | getBits(i, 6, 0)})
+ if size != 2 || r == utf8.RuneError {
+ panic(fmt.Sprintf("Error encoding an int %d with size 2, got rune %v and size %d", size, r, i))
+ }
+ return r
+ }
+
+ // Last -3 here needed because for some reason 3rd to last codepoint 65533 in this range
+ // was returning utf8.RuneError during encoding.
+ if i < ((1 << THREE_BYTE_BITS) - UNICODE_INVALID_RANGE_DELTA - 3) {
+ if i >= UNICODE_INVALID_RANGE_START {
+ i += UNICODE_INVALID_RANGE_DELTA
+ }
+
+ r, size := utf8.DecodeRune([]byte{0b11100000 | getBits(i, 4, 12), 0b10000000 | getBits(i, 6, 6), 0b10000000 | getBits(i, 6, 0)})
+ if size != 3 || r == utf8.RuneError {
+ panic(fmt.Sprintf("Error encoding an int %d with size 3, got rune %v and size %d", size, r, i))
+ }
+ return r
+ }
+
+ if i < (1<= UNICODE_INVALID_RANGE_END {
+ return result - UNICODE_INVALID_RANGE_DELTA
+ }
+
+ return result
+ }
+
+ if size == 4 {
+ result := uint32(bytes[0]&0b111)<<18 | uint32(bytes[1]&0b111111)<<12 | uint32(bytes[2]&0b111111)<<6 | uint32(bytes[3]&0b111111)
+ return result - UNICODE_INVALID_RANGE_DELTA - 3
+ }
+
+ panic(fmt.Sprintf("Unexpected state decoding rune=%v size=%d", r, size))
+}
diff --git a/vendor/github.com/skeema/knownhosts/CONTRIBUTING.md b/vendor/github.com/skeema/knownhosts/CONTRIBUTING.md
new file mode 100644
index 00000000..9624f827
--- /dev/null
+++ b/vendor/github.com/skeema/knownhosts/CONTRIBUTING.md
@@ -0,0 +1,36 @@
+# Contributing to skeema/knownhosts
+
+Thank you for your interest in contributing! This document provides guidelines for submitting pull requests.
+
+### Link to an issue
+
+Before starting the pull request process, initial discussion should take place on a GitHub issue first. For bug reports, the issue should track the open bug and confirm it is reproducible. For feature requests, the issue should cover why the feature is necessary.
+
+In the issue comments, discuss your suggested approach for a fix/implementation, and please wait to get feedback before opening a pull request.
+
+### Test coverage
+
+In general, please provide reasonably thorough test coverage. Whenever possible, your PR should aim to match or improve the overall test coverage percentage of the package. You can run tests and check coverage locally using `go test -cover`. We also have CI automation in GitHub Actions which will comment on each pull request with a coverage percentage.
+
+That said, it is fine to submit an initial draft / work-in-progress PR without coverage, if you are waiting on implementation feedback before writing the tests.
+
+We intentionally avoid hard-coding SSH keys or known_hosts files into the test logic. Instead, the tests generate new keys and then use them to generate a known_hosts file, which is then cached/reused for that overall test run, in order to keep performance reasonable.
+
+### Documentation
+
+Exported types require doc comments. The linter CI step will catch this if missing.
+
+### Backwards compatibility
+
+Because this package is imported by [nearly 7000 repos on GitHub](https://github.com/skeema/knownhosts/network/dependents), we must be very strict about backwards compatibility of exported symbols and function signatures.
+
+Backwards compatibility can be very tricky in some situations. In this case, a maintainer may need to add additional commits to your branch to adjust the approach. Please do not take offense if this occurs; it is sometimes simply faster to implement a refactor on our end directly. When the PR/branch is merged, a merge commit will be used, to ensure your commits appear as-is in the repo history and are still properly credited to you.
+
+### Avoid rewriting core x/crypto/ssh/knownhosts logic
+
+skeema/knownhosts is intended to be a relatively thin *wrapper* around x/crypto/ssh/knownhosts, without duplicating or re-implementing the core known_hosts file parsing and host key handling logic. Importers of this package should be confident that it can be used as a nearly-drop-in replacement for x/crypto/ssh/knownhosts without introducing substantial risk, security flaws, parser differentials, or unexpected behavior changes.
+
+To solve shortcomings in x/crypto/ssh/knownhosts, we try to come up with workarounds that still utilize x/crypto/ssh/knownhosts functionality whenever possible.
+
+Some bugs in x/crypto/ssh/knownhosts do require re-reading the known_hosts file here to solve, but we make that *optional* by offering separate constructors/types with and without that behavior.
+
diff --git a/vendor/github.com/skeema/knownhosts/LICENSE b/vendor/github.com/skeema/knownhosts/LICENSE
new file mode 100644
index 00000000..8dada3ed
--- /dev/null
+++ b/vendor/github.com/skeema/knownhosts/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright {yyyy} {name of copyright owner}
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/vendor/github.com/skeema/knownhosts/NOTICE b/vendor/github.com/skeema/knownhosts/NOTICE
new file mode 100644
index 00000000..a92cb34d
--- /dev/null
+++ b/vendor/github.com/skeema/knownhosts/NOTICE
@@ -0,0 +1,13 @@
+Copyright 2024 Skeema LLC and the Skeema Knownhosts authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/vendor/github.com/skeema/knownhosts/README.md b/vendor/github.com/skeema/knownhosts/README.md
new file mode 100644
index 00000000..046bc0ed
--- /dev/null
+++ b/vendor/github.com/skeema/knownhosts/README.md
@@ -0,0 +1,133 @@
+# knownhosts: enhanced Golang SSH known_hosts management
+
+[](https://github.com/skeema/knownhosts/actions)
+[](https://coveralls.io/r/skeema/knownhosts)
+[](https://pkg.go.dev/github.com/skeema/knownhosts)
+
+
+> This repo is brought to you by [Skeema](https://github.com/skeema/skeema), a
+> declarative pure-SQL schema management system for MySQL and MariaDB. Our
+> premium products include extensive [SSH tunnel](https://www.skeema.io/docs/features/ssh/)
+> functionality, which internally makes use of this package.
+
+Go provides excellent functionality for OpenSSH known_hosts files in its
+external package [golang.org/x/crypto/ssh/knownhosts](https://pkg.go.dev/golang.org/x/crypto/ssh/knownhosts).
+However, that package is somewhat low-level, making it difficult to implement full known_hosts management similar to OpenSSH's command-line behavior. Additionally, [golang.org/x/crypto/ssh/knownhosts](https://pkg.go.dev/golang.org/x/crypto/ssh/knownhosts) has several known issues in edge cases, some of which have remained open for multiple years.
+
+Package [github.com/skeema/knownhosts](https://github.com/skeema/knownhosts) provides a *thin wrapper* around [golang.org/x/crypto/ssh/knownhosts](https://pkg.go.dev/golang.org/x/crypto/ssh/knownhosts), adding the following improvements and fixes without duplicating its core logic:
+
+* Look up known_hosts public keys for any given host
+* Auto-populate ssh.ClientConfig.HostKeyAlgorithms easily based on known_hosts, providing a solution for [golang/go#29286](https://github.com/golang/go/issues/29286). (This also properly handles cert algorithms for hosts using CA keys when [using the NewDB constructor](#enhancements-requiring-extra-parsing) added in skeema/knownhosts v1.3.0.)
+* Properly match wildcard hostname known_hosts entries regardless of port number, providing a solution for [golang/go#52056](https://github.com/golang/go/issues/52056). (Added in v1.3.0; requires [using the NewDB constructor](#enhancements-requiring-extra-parsing))
+* Write new known_hosts entries to an io.Writer
+* Properly format/normalize new known_hosts entries containing ipv6 addresses, providing a solution for [golang/go#53463](https://github.com/golang/go/issues/53463)
+* Easily determine if an ssh.HostKeyCallback's error corresponds to a host whose key has changed (indicating potential MitM attack) vs a host that just isn't known yet
+
+## How host key lookup works
+
+Although [golang.org/x/crypto/ssh/knownhosts](https://pkg.go.dev/golang.org/x/crypto/ssh/knownhosts) doesn't directly expose a way to query its known_host map, we use a subtle trick to do so: invoke the HostKeyCallback with a valid host but a bogus key. The resulting KeyError allows us to determine which public keys are actually present for that host.
+
+By using this technique, [github.com/skeema/knownhosts](https://github.com/skeema/knownhosts) doesn't need to duplicate any of the core known_hosts host-lookup logic from [golang.org/x/crypto/ssh/knownhosts](https://pkg.go.dev/golang.org/x/crypto/ssh/knownhosts).
+
+## Populating ssh.ClientConfig.HostKeyAlgorithms based on known_hosts
+
+Hosts often have multiple public keys, each of a different type (algorithm). This can be [problematic](https://github.com/golang/go/issues/29286) in [golang.org/x/crypto/ssh/knownhosts](https://pkg.go.dev/golang.org/x/crypto/ssh/knownhosts): if a host's first public key is *not* in known_hosts, but a key of a different type *is*, the HostKeyCallback returns an error. The solution is to populate `ssh.ClientConfig.HostKeyAlgorithms` based on the algorithms of the known_hosts entries for that host, but
+[golang.org/x/crypto/ssh/knownhosts](https://pkg.go.dev/golang.org/x/crypto/ssh/knownhosts)
+does not provide an obvious way to do so.
+
+This package uses its host key lookup trick in order to make ssh.ClientConfig.HostKeyAlgorithms easy to populate:
+
+```golang
+import (
+ "golang.org/x/crypto/ssh"
+ "github.com/skeema/knownhosts"
+)
+
+func sshConfigForHost(hostWithPort string) (*ssh.ClientConfig, error) {
+ kh, err := knownhosts.NewDB("/home/myuser/.ssh/known_hosts")
+ if err != nil {
+ return nil, err
+ }
+ config := &ssh.ClientConfig{
+ User: "myuser",
+ Auth: []ssh.AuthMethod{ /* ... */ },
+ HostKeyCallback: kh.HostKeyCallback(),
+ HostKeyAlgorithms: kh.HostKeyAlgorithms(hostWithPort),
+ }
+ return config, nil
+}
+```
+
+## Enhancements requiring extra parsing
+
+Originally, this package did not re-read/re-parse the known_hosts files at all, relying entirely on [golang.org/x/crypto/ssh/knownhosts](https://pkg.go.dev/golang.org/x/crypto/ssh/knownhosts) for all known_hosts file reading and processing. This package only offered a constructor called `New`, returning a host key callback, identical to the call pattern of [golang.org/x/crypto/ssh/knownhosts](https://pkg.go.dev/golang.org/x/crypto/ssh/knownhosts) but with extra methods available on the callback type.
+
+However, a couple shortcomings in [golang.org/x/crypto/ssh/knownhosts](https://pkg.go.dev/golang.org/x/crypto/ssh/knownhosts) cannot possibly be solved without re-reading the known_hosts file. Therefore, as of v1.3.0 of this package, we now offer an alternative constructor `NewDB`, which does an additional read of the known_hosts file (after the one from [golang.org/x/crypto/ssh/knownhosts](https://pkg.go.dev/golang.org/x/crypto/ssh/knownhosts)), in order to detect:
+
+* @cert-authority lines, so that we can correctly return cert key algorithms instead of normal host key algorithms when appropriate
+* host pattern wildcards, so that we can match OpenSSH's behavior for non-standard port numbers, unlike how [golang.org/x/crypto/ssh/knownhosts](https://pkg.go.dev/golang.org/x/crypto/ssh/knownhosts) normally treats them
+
+Aside from *detecting* these special cases, this package otherwise still directly uses [golang.org/x/crypto/ssh/knownhosts](https://pkg.go.dev/golang.org/x/crypto/ssh/knownhosts) for host lookups and all other known_hosts file processing. We do **not** fork or re-implement those core behaviors of [golang.org/x/crypto/ssh/knownhosts](https://pkg.go.dev/golang.org/x/crypto/ssh/knownhosts).
+
+The performance impact of this extra known_hosts read should be minimal, as the file should typically be in the filesystem cache already from the original read by [golang.org/x/crypto/ssh/knownhosts](https://pkg.go.dev/golang.org/x/crypto/ssh/knownhosts). That said, users who wish to avoid the extra read can stay with the `New` constructor, which intentionally retains its pre-v1.3.0 behavior as-is. However, the extra fixes for @cert-authority and host pattern wildcards will not be enabled in that case.
+
+## Writing new known_hosts entries
+
+If you wish to mimic the behavior of OpenSSH's `StrictHostKeyChecking=no` or `StrictHostKeyChecking=ask`, this package provides a few functions to simplify this task. For example:
+
+```golang
+sshHost := "yourserver.com:22"
+khPath := "/home/myuser/.ssh/known_hosts"
+kh, err := knownhosts.NewDB(khPath)
+if err != nil {
+ log.Fatal("Failed to read known_hosts: ", err)
+}
+
+// Create a custom permissive hostkey callback which still errors on hosts
+// with changed keys, but allows unknown hosts and adds them to known_hosts
+cb := ssh.HostKeyCallback(func(hostname string, remote net.Addr, key ssh.PublicKey) error {
+ innerCallback := kh.HostKeyCallback()
+ err := innerCallback(hostname, remote, key)
+ if knownhosts.IsHostKeyChanged(err) {
+ return fmt.Errorf("REMOTE HOST IDENTIFICATION HAS CHANGED for host %s! This may indicate a MitM attack.", hostname)
+ } else if knownhosts.IsHostUnknown(err) {
+ f, ferr := os.OpenFile(khPath, os.O_APPEND|os.O_WRONLY, 0600)
+ if ferr == nil {
+ defer f.Close()
+ ferr = knownhosts.WriteKnownHost(f, hostname, remote, key)
+ }
+ if ferr == nil {
+ log.Printf("Added host %s to known_hosts\n", hostname)
+ } else {
+ log.Printf("Failed to add host %s to known_hosts: %v\n", hostname, ferr)
+ }
+ return nil // permit previously-unknown hosts (warning: may be insecure)
+ }
+ return err
+})
+
+config := &ssh.ClientConfig{
+ User: "myuser",
+ Auth: []ssh.AuthMethod{ /* ... */ },
+ HostKeyCallback: cb,
+ HostKeyAlgorithms: kh.HostKeyAlgorithms(sshHost),
+}
+```
+
+## License
+
+**Source code copyright 2024 Skeema LLC and the Skeema Knownhosts authors**
+
+```text
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+```
diff --git a/vendor/github.com/skeema/knownhosts/knownhosts.go b/vendor/github.com/skeema/knownhosts/knownhosts.go
new file mode 100644
index 00000000..2b7536e0
--- /dev/null
+++ b/vendor/github.com/skeema/knownhosts/knownhosts.go
@@ -0,0 +1,447 @@
+// Package knownhosts is a thin wrapper around golang.org/x/crypto/ssh/knownhosts,
+// adding the ability to obtain the list of host key algorithms for a known host.
+package knownhosts
+
+import (
+ "bufio"
+ "bytes"
+ "encoding/base64"
+ "errors"
+ "fmt"
+ "io"
+ "net"
+ "os"
+ "sort"
+ "strings"
+
+ "golang.org/x/crypto/ssh"
+ xknownhosts "golang.org/x/crypto/ssh/knownhosts"
+)
+
+// HostKeyDB wraps logic in golang.org/x/crypto/ssh/knownhosts with additional
+// behaviors, such as the ability to perform host key/algorithm lookups from
+// known_hosts entries.
+type HostKeyDB struct {
+ callback ssh.HostKeyCallback
+ isCert map[string]bool // keyed by "filename:line"
+ isWildcard map[string]bool // keyed by "filename:line"
+}
+
+// NewDB creates a HostKeyDB from the given OpenSSH known_hosts file(s). It
+// reads and parses the provided files one additional time (beyond logic in
+// golang.org/x/crypto/ssh/knownhosts) in order to:
+//
+// - Handle CA lines properly and return ssh.CertAlgo* values when calling the
+// HostKeyAlgorithms method, for use in ssh.ClientConfig.HostKeyAlgorithms
+// - Allow * wildcards in hostnames to match on non-standard ports, providing
+// a workaround for https://github.com/golang/go/issues/52056 in order to
+// align with OpenSSH's wildcard behavior
+//
+// When supplying multiple files, their order does not matter.
+func NewDB(files ...string) (*HostKeyDB, error) {
+ cb, err := xknownhosts.New(files...)
+ if err != nil {
+ return nil, err
+ }
+ hkdb := &HostKeyDB{
+ callback: cb,
+ isCert: make(map[string]bool),
+ isWildcard: make(map[string]bool),
+ }
+
+ // Re-read each file a single time, looking for @cert-authority lines. The
+ // logic for reading the file is designed to mimic hostKeyDB.Read from
+ // golang.org/x/crypto/ssh/knownhosts
+ for _, filename := range files {
+ f, err := os.Open(filename)
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+ scanner := bufio.NewScanner(f)
+ lineNum := 0
+ for scanner.Scan() {
+ lineNum++
+ line := scanner.Bytes()
+ line = bytes.TrimSpace(line)
+ // Does the line start with "@cert-authority" followed by whitespace?
+ if len(line) > 15 && bytes.HasPrefix(line, []byte("@cert-authority")) && (line[15] == ' ' || line[15] == '\t') {
+ mapKey := fmt.Sprintf("%s:%d", filename, lineNum)
+ hkdb.isCert[mapKey] = true
+ line = bytes.TrimSpace(line[16:])
+ }
+ // truncate line to just the host pattern field
+ if i := bytes.IndexAny(line, "\t "); i >= 0 {
+ line = line[:i]
+ }
+ // Does the host pattern contain a * wildcard and no specific port?
+ if i := bytes.IndexRune(line, '*'); i >= 0 && !bytes.Contains(line[i:], []byte("]:")) {
+ mapKey := fmt.Sprintf("%s:%d", filename, lineNum)
+ hkdb.isWildcard[mapKey] = true
+ }
+ }
+ if err := scanner.Err(); err != nil {
+ return nil, fmt.Errorf("knownhosts: %s:%d: %w", filename, lineNum, err)
+ }
+ }
+ return hkdb, nil
+}
+
+// HostKeyCallback returns an ssh.HostKeyCallback. This can be used directly in
+// ssh.ClientConfig.HostKeyCallback, as shown in the example for NewDB.
+// Alternatively, you can wrap it with an outer callback to potentially handle
+// appending a new entry to the known_hosts file; see example in WriteKnownHost.
+func (hkdb *HostKeyDB) HostKeyCallback() ssh.HostKeyCallback {
+ // Either NewDB found no wildcard host patterns, or hkdb was created from
+ // HostKeyCallback.ToDB in which case we didn't scan known_hosts for them:
+ // return the callback (which came from x/crypto/ssh/knownhosts) as-is
+ if len(hkdb.isWildcard) == 0 {
+ return hkdb.callback
+ }
+
+ // If we scanned for wildcards and found at least one, return a wrapped
+ // callback with extra behavior: if the host lookup found no matches, and the
+ // host arg had a non-standard port, re-do the lookup on standard port 22. If
+ // that second call returns a *xknownhosts.KeyError, filter down any resulting
+ // Want keys to known wildcard entries.
+ f := func(hostname string, remote net.Addr, key ssh.PublicKey) error {
+ callbackErr := hkdb.callback(hostname, remote, key)
+ if callbackErr == nil || IsHostKeyChanged(callbackErr) { // hostname has known_host entries as-is
+ return callbackErr
+ }
+ justHost, port, splitErr := net.SplitHostPort(hostname)
+ if splitErr != nil || port == "" || port == "22" { // hostname already using standard port
+ return callbackErr
+ }
+ // If we reach here, the port was non-standard and no known_host entries
+ // were found for the non-standard port. Try again with standard port.
+ if tcpAddr, ok := remote.(*net.TCPAddr); ok && tcpAddr.Port != 22 {
+ remote = &net.TCPAddr{
+ IP: tcpAddr.IP,
+ Port: 22,
+ Zone: tcpAddr.Zone,
+ }
+ }
+ callbackErr = hkdb.callback(justHost+":22", remote, key)
+ var keyErr *xknownhosts.KeyError
+ if errors.As(callbackErr, &keyErr) && len(keyErr.Want) > 0 {
+ wildcardKeys := make([]xknownhosts.KnownKey, 0, len(keyErr.Want))
+ for _, wantKey := range keyErr.Want {
+ if hkdb.isWildcard[fmt.Sprintf("%s:%d", wantKey.Filename, wantKey.Line)] {
+ wildcardKeys = append(wildcardKeys, wantKey)
+ }
+ }
+ callbackErr = &xknownhosts.KeyError{
+ Want: wildcardKeys,
+ }
+ }
+ return callbackErr
+ }
+ return ssh.HostKeyCallback(f)
+}
+
+// PublicKey wraps ssh.PublicKey with an additional field, to identify
+// whether the key corresponds to a certificate authority.
+type PublicKey struct {
+ ssh.PublicKey
+ Cert bool
+}
+
+// HostKeys returns a slice of known host public keys for the supplied host:port
+// found in the known_hosts file(s), or an empty slice if the host is not
+// already known. For hosts that have multiple known_hosts entries (for
+// different key types), the result will be sorted by known_hosts filename and
+// line number.
+// If hkdb was originally created by calling NewDB, the Cert boolean field of
+// each result entry reports whether the key corresponded to a @cert-authority
+// line. If hkdb was NOT obtained from NewDB, then Cert will always be false.
+func (hkdb *HostKeyDB) HostKeys(hostWithPort string) (keys []PublicKey) {
+ var keyErr *xknownhosts.KeyError
+ placeholderAddr := &net.TCPAddr{IP: []byte{0, 0, 0, 0}}
+ placeholderPubKey := &fakePublicKey{}
+ var kkeys []xknownhosts.KnownKey
+ callback := hkdb.HostKeyCallback()
+ if hkcbErr := callback(hostWithPort, placeholderAddr, placeholderPubKey); errors.As(hkcbErr, &keyErr) {
+ kkeys = append(kkeys, keyErr.Want...)
+ knownKeyLess := func(i, j int) bool {
+ if kkeys[i].Filename < kkeys[j].Filename {
+ return true
+ }
+ return (kkeys[i].Filename == kkeys[j].Filename && kkeys[i].Line < kkeys[j].Line)
+ }
+ sort.Slice(kkeys, knownKeyLess)
+ keys = make([]PublicKey, len(kkeys))
+ for n := range kkeys {
+ keys[n] = PublicKey{
+ PublicKey: kkeys[n].Key,
+ }
+ if len(hkdb.isCert) > 0 {
+ keys[n].Cert = hkdb.isCert[fmt.Sprintf("%s:%d", kkeys[n].Filename, kkeys[n].Line)]
+ }
+ }
+ }
+ return keys
+}
+
+// HostKeyAlgorithms returns a slice of host key algorithms for the supplied
+// host:port found in the known_hosts file(s), or an empty slice if the host
+// is not already known. The result may be used in ssh.ClientConfig's
+// HostKeyAlgorithms field, either as-is or after filtering (if you wish to
+// ignore or prefer particular algorithms). For hosts that have multiple
+// known_hosts entries (of different key types), the result will be sorted by
+// known_hosts filename and line number.
+// If hkdb was originally created by calling NewDB, any @cert-authority lines
+// in the known_hosts file will properly be converted to the corresponding
+// ssh.CertAlgo* values.
+func (hkdb *HostKeyDB) HostKeyAlgorithms(hostWithPort string) (algos []string) {
+ // We ensure that algos never contains duplicates. This is done for robustness
+ // even though currently golang.org/x/crypto/ssh/knownhosts never exposes
+ // multiple keys of the same type. This way our behavior here is unaffected
+ // even if https://github.com/golang/go/issues/28870 is implemented, for
+ // example by https://github.com/golang/crypto/pull/254.
+ hostKeys := hkdb.HostKeys(hostWithPort)
+ seen := make(map[string]struct{}, len(hostKeys))
+ addAlgo := func(typ string, cert bool) {
+ if cert {
+ typ = keyTypeToCertAlgo(typ)
+ }
+ if _, already := seen[typ]; !already {
+ algos = append(algos, typ)
+ seen[typ] = struct{}{}
+ }
+ }
+ for _, key := range hostKeys {
+ typ := key.Type()
+ if typ == ssh.KeyAlgoRSA {
+ // KeyAlgoRSASHA256 and KeyAlgoRSASHA512 are only public key algorithms,
+ // not public key formats, so they can't appear as a PublicKey.Type.
+ // The corresponding PublicKey.Type is KeyAlgoRSA. See RFC 8332, Section 2.
+ addAlgo(ssh.KeyAlgoRSASHA512, key.Cert)
+ addAlgo(ssh.KeyAlgoRSASHA256, key.Cert)
+ }
+ addAlgo(typ, key.Cert)
+ }
+ return algos
+}
+
+func keyTypeToCertAlgo(keyType string) string {
+ switch keyType {
+ case ssh.KeyAlgoRSA:
+ return ssh.CertAlgoRSAv01
+ case ssh.KeyAlgoRSASHA256:
+ return ssh.CertAlgoRSASHA256v01
+ case ssh.KeyAlgoRSASHA512:
+ return ssh.CertAlgoRSASHA512v01
+ case ssh.KeyAlgoDSA:
+ return ssh.CertAlgoDSAv01
+ case ssh.KeyAlgoECDSA256:
+ return ssh.CertAlgoECDSA256v01
+ case ssh.KeyAlgoSKECDSA256:
+ return ssh.CertAlgoSKECDSA256v01
+ case ssh.KeyAlgoECDSA384:
+ return ssh.CertAlgoECDSA384v01
+ case ssh.KeyAlgoECDSA521:
+ return ssh.CertAlgoECDSA521v01
+ case ssh.KeyAlgoED25519:
+ return ssh.CertAlgoED25519v01
+ case ssh.KeyAlgoSKED25519:
+ return ssh.CertAlgoSKED25519v01
+ }
+ return ""
+}
+
+// HostKeyCallback wraps ssh.HostKeyCallback with additional methods to
+// perform host key and algorithm lookups from the known_hosts entries. It is
+// otherwise identical to ssh.HostKeyCallback, and does not introduce any file-
+// parsing behavior beyond what is in golang.org/x/crypto/ssh/knownhosts.
+//
+// In most situations, use HostKeyDB and its constructor NewDB instead of using
+// the HostKeyCallback type. The HostKeyCallback type is only provided for
+// backwards compatibility with older versions of this package, as well as for
+// very strict situations where any extra known_hosts file-parsing is
+// undesirable.
+//
+// Methods of HostKeyCallback do not provide any special treatment for
+// @cert-authority lines, which will (incorrectly) look like normal non-CA host
+// keys. Additionally, HostKeyCallback lacks the fix for applying * wildcard
+// known_host entries to all ports, like OpenSSH's behavior.
+type HostKeyCallback ssh.HostKeyCallback
+
+// New creates a HostKeyCallback from the given OpenSSH known_hosts file(s). The
+// returned value may be used in ssh.ClientConfig.HostKeyCallback by casting it
+// to ssh.HostKeyCallback, or using its HostKeyCallback method. Otherwise, it
+// operates the same as the New function in golang.org/x/crypto/ssh/knownhosts.
+// When supplying multiple files, their order does not matter.
+//
+// In most situations, you should avoid this function, as the returned value
+// lacks several enhanced behaviors. See doc comment for HostKeyCallback for
+// more information. Instead, most callers should use NewDB to create a
+// HostKeyDB, which includes these enhancements.
+func New(files ...string) (HostKeyCallback, error) {
+ cb, err := xknownhosts.New(files...)
+ return HostKeyCallback(cb), err
+}
+
+// HostKeyCallback simply casts the receiver back to ssh.HostKeyCallback, for
+// use in ssh.ClientConfig.HostKeyCallback.
+func (hkcb HostKeyCallback) HostKeyCallback() ssh.HostKeyCallback {
+ return ssh.HostKeyCallback(hkcb)
+}
+
+// ToDB converts the receiver into a HostKeyDB. However, the returned HostKeyDB
+// lacks the enhanced behaviors described in the doc comment for NewDB: proper
+// CA support, and wildcard matching on nonstandard ports.
+//
+// It is generally preferable to create a HostKeyDB by using NewDB. The ToDB
+// method is only provided for situations in which the calling code needs to
+// make the extra NewDB behaviors optional / user-configurable, perhaps for
+// reasons of performance or code trust (since NewDB reads the known_host file
+// an extra time, which may be undesirable in some strict situations). This way,
+// callers can conditionally create a non-enhanced HostKeyDB by using New and
+// ToDB. See code example.
+func (hkcb HostKeyCallback) ToDB() *HostKeyDB {
+ // This intentionally leaves the isCert and isWildcard map fields as nil, as
+ // there is no way to retroactively populate them from just a HostKeyCallback.
+ // Methods of HostKeyDB will skip any related enhanced behaviors accordingly.
+ return &HostKeyDB{callback: ssh.HostKeyCallback(hkcb)}
+}
+
+// HostKeys returns a slice of known host public keys for the supplied host:port
+// found in the known_hosts file(s), or an empty slice if the host is not
+// already known. For hosts that have multiple known_hosts entries (for
+// different key types), the result will be sorted by known_hosts filename and
+// line number.
+// In the returned values, there is no way to distinguish between CA keys
+// (known_hosts lines beginning with @cert-authority) and regular keys. To do
+// so, see NewDB and HostKeyDB.HostKeys instead.
+func (hkcb HostKeyCallback) HostKeys(hostWithPort string) []ssh.PublicKey {
+ annotatedKeys := hkcb.ToDB().HostKeys(hostWithPort)
+ rawKeys := make([]ssh.PublicKey, len(annotatedKeys))
+ for n, ak := range annotatedKeys {
+ rawKeys[n] = ak.PublicKey
+ }
+ return rawKeys
+}
+
+// HostKeyAlgorithms returns a slice of host key algorithms for the supplied
+// host:port found in the known_hosts file(s), or an empty slice if the host
+// is not already known. The result may be used in ssh.ClientConfig's
+// HostKeyAlgorithms field, either as-is or after filtering (if you wish to
+// ignore or prefer particular algorithms). For hosts that have multiple
+// known_hosts entries (for different key types), the result will be sorted by
+// known_hosts filename and line number.
+// The returned values will not include ssh.CertAlgo* values. If any
+// known_hosts lines had @cert-authority prefixes, their original key algo will
+// be returned instead. For proper CA support, see NewDB and
+// HostKeyDB.HostKeyAlgorithms instead.
+func (hkcb HostKeyCallback) HostKeyAlgorithms(hostWithPort string) (algos []string) {
+ return hkcb.ToDB().HostKeyAlgorithms(hostWithPort)
+}
+
+// HostKeyAlgorithms is a convenience function for performing host key algorithm
+// lookups on an ssh.HostKeyCallback directly. It is intended for use in code
+// paths that stay with the New method of golang.org/x/crypto/ssh/knownhosts
+// rather than this package's New or NewDB methods.
+// The returned values will not include ssh.CertAlgo* values. If any
+// known_hosts lines had @cert-authority prefixes, their original key algo will
+// be returned instead. For proper CA support, see NewDB and
+// HostKeyDB.HostKeyAlgorithms instead.
+func HostKeyAlgorithms(cb ssh.HostKeyCallback, hostWithPort string) []string {
+ return HostKeyCallback(cb).HostKeyAlgorithms(hostWithPort)
+}
+
+// IsHostKeyChanged returns a boolean indicating whether the error indicates
+// the host key has changed. It is intended to be called on the error returned
+// from invoking a host key callback, to check whether an SSH host is known.
+func IsHostKeyChanged(err error) bool {
+ var keyErr *xknownhosts.KeyError
+ return errors.As(err, &keyErr) && len(keyErr.Want) > 0
+}
+
+// IsHostUnknown returns a boolean indicating whether the error represents an
+// unknown host. It is intended to be called on the error returned from invoking
+// a host key callback to check whether an SSH host is known.
+func IsHostUnknown(err error) bool {
+ var keyErr *xknownhosts.KeyError
+ return errors.As(err, &keyErr) && len(keyErr.Want) == 0
+}
+
+// Normalize normalizes an address into the form used in known_hosts. This
+// implementation includes a fix for https://github.com/golang/go/issues/53463
+// and will omit brackets around ipv6 addresses on standard port 22.
+func Normalize(address string) string {
+ host, port, err := net.SplitHostPort(address)
+ if err != nil {
+ host = address
+ port = "22"
+ }
+ entry := host
+ if port != "22" {
+ entry = "[" + entry + "]:" + port
+ } else if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") {
+ entry = entry[1 : len(entry)-1]
+ }
+ return entry
+}
+
+// Line returns a line to append to the known_hosts files. This implementation
+// uses the local patched implementation of Normalize in order to solve
+// https://github.com/golang/go/issues/53463.
+func Line(addresses []string, key ssh.PublicKey) string {
+ var trimmed []string
+ for _, a := range addresses {
+ trimmed = append(trimmed, Normalize(a))
+ }
+
+ return strings.Join([]string{
+ strings.Join(trimmed, ","),
+ key.Type(),
+ base64.StdEncoding.EncodeToString(key.Marshal()),
+ }, " ")
+}
+
+// WriteKnownHost writes a known_hosts line to w for the supplied hostname,
+// remote, and key. This is useful when writing a custom hostkey callback which
+// wraps a callback obtained from this package to provide additional known_hosts
+// management functionality. The hostname, remote, and key typically correspond
+// to the callback's args. This function does not support writing
+// @cert-authority lines.
+func WriteKnownHost(w io.Writer, hostname string, remote net.Addr, key ssh.PublicKey) error {
+ // Always include hostname; only also include remote if it isn't a zero value
+ // and doesn't normalize to the same string as hostname.
+ hostnameNormalized := Normalize(hostname)
+ if strings.ContainsAny(hostnameNormalized, "\t ") {
+ return fmt.Errorf("knownhosts: hostname '%s' contains spaces", hostnameNormalized)
+ }
+ addresses := []string{hostnameNormalized}
+ remoteStrNormalized := Normalize(remote.String())
+ if remoteStrNormalized != "[0.0.0.0]:0" && remoteStrNormalized != hostnameNormalized &&
+ !strings.ContainsAny(remoteStrNormalized, "\t ") {
+ addresses = append(addresses, remoteStrNormalized)
+ }
+ line := Line(addresses, key) + "\n"
+ _, err := w.Write([]byte(line))
+ return err
+}
+
+// WriteKnownHostCA writes a @cert-authority line to w for the supplied host
+// name/pattern and key.
+func WriteKnownHostCA(w io.Writer, hostPattern string, key ssh.PublicKey) error {
+ encodedKey := base64.StdEncoding.EncodeToString(key.Marshal())
+ _, err := fmt.Fprintf(w, "@cert-authority %s %s %s\n", hostPattern, key.Type(), encodedKey)
+ return err
+}
+
+// fakePublicKey is used as part of the work-around for
+// https://github.com/golang/go/issues/29286
+type fakePublicKey struct{}
+
+func (fakePublicKey) Type() string {
+ return "fake-public-key"
+}
+func (fakePublicKey) Marshal() []byte {
+ return []byte("fake public key")
+}
+func (fakePublicKey) Verify(_ []byte, _ *ssh.Signature) error {
+ return errors.New("Verify called on placeholder key")
+}
diff --git a/vendor/github.com/sourcegraph/conc/.golangci.yml b/vendor/github.com/sourcegraph/conc/.golangci.yml
new file mode 100644
index 00000000..ae65a760
--- /dev/null
+++ b/vendor/github.com/sourcegraph/conc/.golangci.yml
@@ -0,0 +1,11 @@
+linters:
+ disable-all: true
+ enable:
+ - errcheck
+ - godot
+ - gosimple
+ - govet
+ - ineffassign
+ - staticcheck
+ - typecheck
+ - unused
diff --git a/vendor/github.com/sourcegraph/conc/LICENSE b/vendor/github.com/sourcegraph/conc/LICENSE
new file mode 100644
index 00000000..1081f4ef
--- /dev/null
+++ b/vendor/github.com/sourcegraph/conc/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2023 Sourcegraph
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/github.com/sourcegraph/conc/README.md b/vendor/github.com/sourcegraph/conc/README.md
new file mode 100644
index 00000000..1c87c3c9
--- /dev/null
+++ b/vendor/github.com/sourcegraph/conc/README.md
@@ -0,0 +1,464 @@
+
+
+# `conc`: better structured concurrency for go
+
+[](https://pkg.go.dev/github.com/sourcegraph/conc)
+[](https://sourcegraph.com/github.com/sourcegraph/conc)
+[](https://goreportcard.com/report/github.com/sourcegraph/conc)
+[](https://codecov.io/gh/sourcegraph/conc)
+[](https://discord.gg/bvXQXmtRjN)
+
+`conc` is your toolbelt for structured concurrency in go, making common tasks
+easier and safer.
+
+```sh
+go get github.com/sourcegraph/conc
+```
+
+# At a glance
+
+- Use [`conc.WaitGroup`](https://pkg.go.dev/github.com/sourcegraph/conc#WaitGroup) if you just want a safer version of `sync.WaitGroup`
+- Use [`pool.Pool`](https://pkg.go.dev/github.com/sourcegraph/conc/pool#Pool) if you want a concurrency-limited task runner
+- Use [`pool.ResultPool`](https://pkg.go.dev/github.com/sourcegraph/conc/pool#ResultPool) if you want a concurrent task runner that collects task results
+- Use [`pool.(Result)?ErrorPool`](https://pkg.go.dev/github.com/sourcegraph/conc/pool#ErrorPool) if your tasks are fallible
+- Use [`pool.(Result)?ContextPool`](https://pkg.go.dev/github.com/sourcegraph/conc/pool#ContextPool) if your tasks should be canceled on failure
+- Use [`stream.Stream`](https://pkg.go.dev/github.com/sourcegraph/conc/stream#Stream) if you want to process an ordered stream of tasks in parallel with serial callbacks
+- Use [`iter.Map`](https://pkg.go.dev/github.com/sourcegraph/conc/iter#Map) if you want to concurrently map a slice
+- Use [`iter.ForEach`](https://pkg.go.dev/github.com/sourcegraph/conc/iter#ForEach) if you want to concurrently iterate over a slice
+- Use [`panics.Catcher`](https://pkg.go.dev/github.com/sourcegraph/conc/panics#Catcher) if you want to catch panics in your own goroutines
+
+All pools are created with
+[`pool.New()`](https://pkg.go.dev/github.com/sourcegraph/conc/pool#New)
+or
+[`pool.NewWithResults[T]()`](https://pkg.go.dev/github.com/sourcegraph/conc/pool#NewWithResults),
+then configured with methods:
+
+- [`p.WithMaxGoroutines()`](https://pkg.go.dev/github.com/sourcegraph/conc/pool#Pool.MaxGoroutines) configures the maximum number of goroutines in the pool
+- [`p.WithErrors()`](https://pkg.go.dev/github.com/sourcegraph/conc/pool#Pool.WithErrors) configures the pool to run tasks that return errors
+- [`p.WithContext(ctx)`](https://pkg.go.dev/github.com/sourcegraph/conc/pool#Pool.WithContext) configures the pool to run tasks that should be canceled on first error
+- [`p.WithFirstError()`](https://pkg.go.dev/github.com/sourcegraph/conc/pool#ErrorPool.WithFirstError) configures error pools to only keep the first returned error rather than an aggregated error
+- [`p.WithCollectErrored()`](https://pkg.go.dev/github.com/sourcegraph/conc/pool#ResultContextPool.WithCollectErrored) configures result pools to collect results even when the task errored
+
+# Goals
+
+The main goals of the package are:
+1) Make it harder to leak goroutines
+2) Handle panics gracefully
+3) Make concurrent code easier to read
+
+## Goal #1: Make it harder to leak goroutines
+
+A common pain point when working with goroutines is cleaning them up. It's
+really easy to fire off a `go` statement and fail to properly wait for it to
+complete.
+
+`conc` takes the opinionated stance that all concurrency should be scoped.
+That is, goroutines should have an owner and that owner should always
+ensure that its owned goroutines exit properly.
+
+In `conc`, the owner of a goroutine is always a `conc.WaitGroup`. Goroutines
+are spawned in a `WaitGroup` with `(*WaitGroup).Go()`, and
+`(*WaitGroup).Wait()` should always be called before the `WaitGroup` goes out
+of scope.
+
+In some cases, you might want a spawned goroutine to outlast the scope of the
+caller. In that case, you could pass a `WaitGroup` into the spawning function.
+
+```go
+func main() {
+ var wg conc.WaitGroup
+ defer wg.Wait()
+
+ startTheThing(&wg)
+}
+
+func startTheThing(wg *conc.WaitGroup) {
+ wg.Go(func() { ... })
+}
+```
+
+For some more discussion on why scoped concurrency is nice, check out [this
+blog
+post](https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/).
+
+## Goal #2: Handle panics gracefully
+
+A frequent problem with goroutines in long-running applications is handling
+panics. A goroutine spawned without a panic handler will crash the whole process
+on panic. This is usually undesirable.
+
+However, if you do add a panic handler to a goroutine, what do you do with the
+panic once you catch it? Some options:
+1) Ignore it
+2) Log it
+3) Turn it into an error and return that to the goroutine spawner
+4) Propagate the panic to the goroutine spawner
+
+Ignoring panics is a bad idea since panics usually mean there is actually
+something wrong and someone should fix it.
+
+Just logging panics isn't great either because then there is no indication to the spawner
+that something bad happened, and it might just continue on as normal even though your
+program is in a really bad state.
+
+Both (3) and (4) are reasonable options, but both require the goroutine to have
+an owner that can actually receive the message that something went wrong. This
+is generally not true with a goroutine spawned with `go`, but in the `conc`
+package, all goroutines have an owner that must collect the spawned goroutine.
+In the conc package, any call to `Wait()` will panic if any of the spawned goroutines
+panicked. Additionally, it decorates the panic value with a stacktrace from the child
+goroutine so that you don't lose information about what caused the panic.
+
+Doing this all correctly every time you spawn something with `go` is not
+trivial and it requires a lot of boilerplate that makes the important parts of
+the code more difficult to read, so `conc` does this for you.
+
+
+
+stdlib |
+conc |
+
+
+|
+
+```go
+type caughtPanicError struct {
+ val any
+ stack []byte
+}
+
+func (e *caughtPanicError) Error() string {
+ return fmt.Sprintf(
+ "panic: %q\n%s",
+ e.val,
+ string(e.stack)
+ )
+}
+
+func main() {
+ done := make(chan error)
+ go func() {
+ defer func() {
+ if v := recover(); v != nil {
+ done <- &caughtPanicError{
+ val: v,
+ stack: debug.Stack()
+ }
+ } else {
+ done <- nil
+ }
+ }()
+ doSomethingThatMightPanic()
+ }()
+ err := <-done
+ if err != nil {
+ panic(err)
+ }
+}
+```
+ |
+
+
+```go
+func main() {
+ var wg conc.WaitGroup
+ wg.Go(doSomethingThatMightPanic)
+ // panics with a nice stacktrace
+ wg.Wait()
+}
+```
+ |
+
+
+
+## Goal #3: Make concurrent code easier to read
+
+Doing concurrency correctly is difficult. Doing it in a way that doesn't
+obfuscate what the code is actually doing is more difficult. The `conc` package
+attempts to make common operations easier by abstracting as much boilerplate
+complexity as possible.
+
+Want to run a set of concurrent tasks with a bounded set of goroutines? Use
+`pool.New()`. Want to process an ordered stream of results concurrently, but
+still maintain order? Try `stream.New()`. What about a concurrent map over
+a slice? Take a peek at `iter.Map()`.
+
+Browse some examples below for some comparisons with doing these by hand.
+
+# Examples
+
+Each of these examples forgoes propagating panics for simplicity. To see
+what kind of complexity that would add, check out the "Goal #2" header above.
+
+Spawn a set of goroutines and waiting for them to finish:
+
+
+
+stdlib |
+conc |
+
+
+|
+
+```go
+func main() {
+ var wg sync.WaitGroup
+ for i := 0; i < 10; i++ {
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ // crashes on panic!
+ doSomething()
+ }()
+ }
+ wg.Wait()
+}
+```
+ |
+
+
+```go
+func main() {
+ var wg conc.WaitGroup
+ for i := 0; i < 10; i++ {
+ wg.Go(doSomething)
+ }
+ wg.Wait()
+}
+```
+ |
+
+
+
+Process each element of a stream in a static pool of goroutines:
+
+
+
+stdlib |
+conc |
+
+
+|
+
+```go
+func process(stream chan int) {
+ var wg sync.WaitGroup
+ for i := 0; i < 10; i++ {
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ for elem := range stream {
+ handle(elem)
+ }
+ }()
+ }
+ wg.Wait()
+}
+```
+ |
+
+
+```go
+func process(stream chan int) {
+ p := pool.New().WithMaxGoroutines(10)
+ for elem := range stream {
+ elem := elem
+ p.Go(func() {
+ handle(elem)
+ })
+ }
+ p.Wait()
+}
+```
+ |
+
+
+
+Process each element of a slice in a static pool of goroutines:
+
+
+
+stdlib |
+conc |
+
+
+|
+
+```go
+func process(values []int) {
+ feeder := make(chan int, 8)
+
+ var wg sync.WaitGroup
+ for i := 0; i < 10; i++ {
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ for elem := range feeder {
+ handle(elem)
+ }
+ }()
+ }
+
+ for _, value := range values {
+ feeder <- value
+ }
+ close(feeder)
+ wg.Wait()
+}
+```
+ |
+
+
+```go
+func process(values []int) {
+ iter.ForEach(values, handle)
+}
+```
+ |
+
+
+
+Concurrently map a slice:
+
+
+
+stdlib |
+conc |
+
+
+|
+
+```go
+func concMap(
+ input []int,
+ f func(int) int,
+) []int {
+ res := make([]int, len(input))
+ var idx atomic.Int64
+
+ var wg sync.WaitGroup
+ for i := 0; i < 10; i++ {
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+
+ for {
+ i := int(idx.Add(1) - 1)
+ if i >= len(input) {
+ return
+ }
+
+ res[i] = f(input[i])
+ }
+ }()
+ }
+ wg.Wait()
+ return res
+}
+```
+ |
+
+
+```go
+func concMap(
+ input []int,
+ f func(*int) int,
+) []int {
+ return iter.Map(input, f)
+}
+```
+ |
+
+
+
+Process an ordered stream concurrently:
+
+
+
+
+stdlib |
+conc |
+
+
+|
+
+```go
+func mapStream(
+ in chan int,
+ out chan int,
+ f func(int) int,
+) {
+ tasks := make(chan func())
+ taskResults := make(chan chan int)
+
+ // Worker goroutines
+ var workerWg sync.WaitGroup
+ for i := 0; i < 10; i++ {
+ workerWg.Add(1)
+ go func() {
+ defer workerWg.Done()
+ for task := range tasks {
+ task()
+ }
+ }()
+ }
+
+ // Ordered reader goroutines
+ var readerWg sync.WaitGroup
+ readerWg.Add(1)
+ go func() {
+ defer readerWg.Done()
+ for result := range taskResults {
+ item := <-result
+ out <- item
+ }
+ }()
+
+ // Feed the workers with tasks
+ for elem := range in {
+ resultCh := make(chan int, 1)
+ taskResults <- resultCh
+ tasks <- func() {
+ resultCh <- f(elem)
+ }
+ }
+
+ // We've exhausted input.
+ // Wait for everything to finish
+ close(tasks)
+ workerWg.Wait()
+ close(taskResults)
+ readerWg.Wait()
+}
+```
+ |
+
+
+```go
+func mapStream(
+ in chan int,
+ out chan int,
+ f func(int) int,
+) {
+ s := stream.New().WithMaxGoroutines(10)
+ for elem := range in {
+ elem := elem
+ s.Go(func() stream.Callback {
+ res := f(elem)
+ return func() { out <- res }
+ })
+ }
+ s.Wait()
+}
+```
+ |
+
+
+
+# Status
+
+This package is currently pre-1.0. There are likely to be minor breaking
+changes before a 1.0 release as we stabilize the APIs and tweak defaults.
+Please open an issue if you have questions, concerns, or requests that you'd
+like addressed before the 1.0 release. Currently, a 1.0 is targeted for
+March 2023.
diff --git a/vendor/github.com/sourcegraph/conc/internal/multierror/multierror_go119.go b/vendor/github.com/sourcegraph/conc/internal/multierror/multierror_go119.go
new file mode 100644
index 00000000..7087e32a
--- /dev/null
+++ b/vendor/github.com/sourcegraph/conc/internal/multierror/multierror_go119.go
@@ -0,0 +1,10 @@
+//go:build !go1.20
+// +build !go1.20
+
+package multierror
+
+import "go.uber.org/multierr"
+
+var (
+ Join = multierr.Combine
+)
diff --git a/vendor/github.com/sourcegraph/conc/internal/multierror/multierror_go120.go b/vendor/github.com/sourcegraph/conc/internal/multierror/multierror_go120.go
new file mode 100644
index 00000000..39cff829
--- /dev/null
+++ b/vendor/github.com/sourcegraph/conc/internal/multierror/multierror_go120.go
@@ -0,0 +1,10 @@
+//go:build go1.20
+// +build go1.20
+
+package multierror
+
+import "errors"
+
+var (
+ Join = errors.Join
+)
diff --git a/vendor/github.com/sourcegraph/conc/iter/iter.go b/vendor/github.com/sourcegraph/conc/iter/iter.go
new file mode 100644
index 00000000..124b4f94
--- /dev/null
+++ b/vendor/github.com/sourcegraph/conc/iter/iter.go
@@ -0,0 +1,85 @@
+package iter
+
+import (
+ "runtime"
+ "sync/atomic"
+
+ "github.com/sourcegraph/conc"
+)
+
+// defaultMaxGoroutines returns the default maximum number of
+// goroutines to use within this package.
+func defaultMaxGoroutines() int { return runtime.GOMAXPROCS(0) }
+
+// Iterator can be used to configure the behaviour of ForEach
+// and ForEachIdx. The zero value is safe to use with reasonable
+// defaults.
+//
+// Iterator is also safe for reuse and concurrent use.
+type Iterator[T any] struct {
+ // MaxGoroutines controls the maximum number of goroutines
+ // to use on this Iterator's methods.
+ //
+ // If unset, MaxGoroutines defaults to runtime.GOMAXPROCS(0).
+ MaxGoroutines int
+}
+
+// ForEach executes f in parallel over each element in input.
+//
+// It is safe to mutate the input parameter, which makes it
+// possible to map in place.
+//
+// ForEach always uses at most runtime.GOMAXPROCS goroutines.
+// It takes roughly 2µs to start up the goroutines and adds
+// an overhead of roughly 50ns per element of input. For
+// a configurable goroutine limit, use a custom Iterator.
+func ForEach[T any](input []T, f func(*T)) { Iterator[T]{}.ForEach(input, f) }
+
+// ForEach executes f in parallel over each element in input,
+// using up to the Iterator's configured maximum number of
+// goroutines.
+//
+// It is safe to mutate the input parameter, which makes it
+// possible to map in place.
+//
+// It takes roughly 2µs to start up the goroutines and adds
+// an overhead of roughly 50ns per element of input.
+func (iter Iterator[T]) ForEach(input []T, f func(*T)) {
+ iter.ForEachIdx(input, func(_ int, t *T) {
+ f(t)
+ })
+}
+
+// ForEachIdx is the same as ForEach except it also provides the
+// index of the element to the callback.
+func ForEachIdx[T any](input []T, f func(int, *T)) { Iterator[T]{}.ForEachIdx(input, f) }
+
+// ForEachIdx is the same as ForEach except it also provides the
+// index of the element to the callback.
+func (iter Iterator[T]) ForEachIdx(input []T, f func(int, *T)) {
+ if iter.MaxGoroutines == 0 {
+ // iter is a value receiver and is hence safe to mutate
+ iter.MaxGoroutines = defaultMaxGoroutines()
+ }
+
+ numInput := len(input)
+ if iter.MaxGoroutines > numInput {
+ // No more concurrent tasks than the number of input items.
+ iter.MaxGoroutines = numInput
+ }
+
+ var idx atomic.Int64
+ // Create the task outside the loop to avoid extra closure allocations.
+ task := func() {
+ i := int(idx.Add(1) - 1)
+ for ; i < numInput; i = int(idx.Add(1) - 1) {
+ f(i, &input[i])
+ }
+ }
+
+ var wg conc.WaitGroup
+ for i := 0; i < iter.MaxGoroutines; i++ {
+ wg.Go(task)
+ }
+ wg.Wait()
+}
diff --git a/vendor/github.com/sourcegraph/conc/iter/map.go b/vendor/github.com/sourcegraph/conc/iter/map.go
new file mode 100644
index 00000000..efbe6bfa
--- /dev/null
+++ b/vendor/github.com/sourcegraph/conc/iter/map.go
@@ -0,0 +1,65 @@
+package iter
+
+import (
+ "sync"
+
+ "github.com/sourcegraph/conc/internal/multierror"
+)
+
+// Mapper is an Iterator with a result type R. It can be used to configure
+// the behaviour of Map and MapErr. The zero value is safe to use with
+// reasonable defaults.
+//
+// Mapper is also safe for reuse and concurrent use.
+type Mapper[T, R any] Iterator[T]
+
+// Map applies f to each element of input, returning the mapped result.
+//
+// Map always uses at most runtime.GOMAXPROCS goroutines. For a configurable
+// goroutine limit, use a custom Mapper.
+func Map[T, R any](input []T, f func(*T) R) []R {
+ return Mapper[T, R]{}.Map(input, f)
+}
+
+// Map applies f to each element of input, returning the mapped result.
+//
+// Map uses up to the configured Mapper's maximum number of goroutines.
+func (m Mapper[T, R]) Map(input []T, f func(*T) R) []R {
+ res := make([]R, len(input))
+ Iterator[T](m).ForEachIdx(input, func(i int, t *T) {
+ res[i] = f(t)
+ })
+ return res
+}
+
+// MapErr applies f to each element of the input, returning the mapped result
+// and a combined error of all returned errors.
+//
+// Map always uses at most runtime.GOMAXPROCS goroutines. For a configurable
+// goroutine limit, use a custom Mapper.
+func MapErr[T, R any](input []T, f func(*T) (R, error)) ([]R, error) {
+ return Mapper[T, R]{}.MapErr(input, f)
+}
+
+// MapErr applies f to each element of the input, returning the mapped result
+// and a combined error of all returned errors.
+//
+// Map uses up to the configured Mapper's maximum number of goroutines.
+func (m Mapper[T, R]) MapErr(input []T, f func(*T) (R, error)) ([]R, error) {
+ var (
+ res = make([]R, len(input))
+ errMux sync.Mutex
+ errs error
+ )
+ Iterator[T](m).ForEachIdx(input, func(i int, t *T) {
+ var err error
+ res[i], err = f(t)
+ if err != nil {
+ errMux.Lock()
+ // TODO: use stdlib errors once multierrors land in go 1.20
+ errs = multierror.Join(errs, err)
+ errMux.Unlock()
+ }
+ })
+ return res, errs
+}
diff --git a/vendor/github.com/sourcegraph/conc/panics/panics.go b/vendor/github.com/sourcegraph/conc/panics/panics.go
new file mode 100644
index 00000000..abbed7fa
--- /dev/null
+++ b/vendor/github.com/sourcegraph/conc/panics/panics.go
@@ -0,0 +1,102 @@
+package panics
+
+import (
+ "fmt"
+ "runtime"
+ "runtime/debug"
+ "sync/atomic"
+)
+
+// Catcher is used to catch panics. You can execute a function with Try,
+// which will catch any spawned panic. Try can be called any number of times,
+// from any number of goroutines. Once all calls to Try have completed, you can
+// get the value of the first panic (if any) with Recovered(), or you can just
+// propagate the panic (re-panic) with Repanic().
+type Catcher struct {
+ recovered atomic.Pointer[Recovered]
+}
+
+// Try executes f, catching any panic it might spawn. It is safe
+// to call from multiple goroutines simultaneously.
+func (p *Catcher) Try(f func()) {
+ defer p.tryRecover()
+ f()
+}
+
+func (p *Catcher) tryRecover() {
+ if val := recover(); val != nil {
+ rp := NewRecovered(1, val)
+ p.recovered.CompareAndSwap(nil, &rp)
+ }
+}
+
+// Repanic panics if any calls to Try caught a panic. It will panic with the
+// value of the first panic caught, wrapped in a panics.Recovered with caller
+// information.
+func (p *Catcher) Repanic() {
+ if val := p.Recovered(); val != nil {
+ panic(val)
+ }
+}
+
+// Recovered returns the value of the first panic caught by Try, or nil if
+// no calls to Try panicked.
+func (p *Catcher) Recovered() *Recovered {
+ return p.recovered.Load()
+}
+
+// NewRecovered creates a panics.Recovered from a panic value and a collected
+// stacktrace. The skip parameter allows the caller to skip stack frames when
+// collecting the stacktrace. Calling with a skip of 0 means include the call to
+// NewRecovered in the stacktrace.
+func NewRecovered(skip int, value any) Recovered {
+ // 64 frames should be plenty
+ var callers [64]uintptr
+ n := runtime.Callers(skip+1, callers[:])
+ return Recovered{
+ Value: value,
+ Callers: callers[:n],
+ Stack: debug.Stack(),
+ }
+}
+
+// Recovered is a panic that was caught with recover().
+type Recovered struct {
+ // The original value of the panic.
+ Value any
+ // The caller list as returned by runtime.Callers when the panic was
+ // recovered. Can be used to produce a more detailed stack information with
+ // runtime.CallersFrames.
+ Callers []uintptr
+ // The formatted stacktrace from the goroutine where the panic was recovered.
+ // Easier to use than Callers.
+ Stack []byte
+}
+
+// String renders a human-readable formatting of the panic.
+func (p *Recovered) String() string {
+ return fmt.Sprintf("panic: %v\nstacktrace:\n%s\n", p.Value, p.Stack)
+}
+
+// AsError casts the panic into an error implementation. The implementation
+// is unwrappable with the cause of the panic, if the panic was provided one.
+func (p *Recovered) AsError() error {
+ if p == nil {
+ return nil
+ }
+ return &ErrRecovered{*p}
+}
+
+// ErrRecovered wraps a panics.Recovered in an error implementation.
+type ErrRecovered struct{ Recovered }
+
+var _ error = (*ErrRecovered)(nil)
+
+func (p *ErrRecovered) Error() string { return p.String() }
+
+func (p *ErrRecovered) Unwrap() error {
+ if err, ok := p.Value.(error); ok {
+ return err
+ }
+ return nil
+}
diff --git a/vendor/github.com/sourcegraph/conc/panics/try.go b/vendor/github.com/sourcegraph/conc/panics/try.go
new file mode 100644
index 00000000..4ded92a1
--- /dev/null
+++ b/vendor/github.com/sourcegraph/conc/panics/try.go
@@ -0,0 +1,11 @@
+package panics
+
+// Try executes f, catching and returning any panic it might spawn.
+//
+// The recovered panic can be propagated with panic(), or handled as a normal error with
+// (*panics.Recovered).AsError().
+func Try(f func()) *Recovered {
+ var c Catcher
+ c.Try(f)
+ return c.Recovered()
+}
diff --git a/vendor/github.com/sourcegraph/conc/waitgroup.go b/vendor/github.com/sourcegraph/conc/waitgroup.go
new file mode 100644
index 00000000..47b1bc1a
--- /dev/null
+++ b/vendor/github.com/sourcegraph/conc/waitgroup.go
@@ -0,0 +1,52 @@
+package conc
+
+import (
+ "sync"
+
+ "github.com/sourcegraph/conc/panics"
+)
+
+// NewWaitGroup creates a new WaitGroup.
+func NewWaitGroup() *WaitGroup {
+ return &WaitGroup{}
+}
+
+// WaitGroup is the primary building block for scoped concurrency.
+// Goroutines can be spawned in the WaitGroup with the Go method,
+// and calling Wait() will ensure that each of those goroutines exits
+// before continuing. Any panics in a child goroutine will be caught
+// and propagated to the caller of Wait().
+//
+// The zero value of WaitGroup is usable, just like sync.WaitGroup.
+// Also like sync.WaitGroup, it must not be copied after first use.
+type WaitGroup struct {
+ wg sync.WaitGroup
+ pc panics.Catcher
+}
+
+// Go spawns a new goroutine in the WaitGroup.
+func (h *WaitGroup) Go(f func()) {
+ h.wg.Add(1)
+ go func() {
+ defer h.wg.Done()
+ h.pc.Try(f)
+ }()
+}
+
+// Wait will block until all goroutines spawned with Go exit and will
+// propagate any panics spawned in a child goroutine.
+func (h *WaitGroup) Wait() {
+ h.wg.Wait()
+
+ // Propagate a panic if we caught one from a child goroutine.
+ h.pc.Repanic()
+}
+
+// WaitAndRecover will block until all goroutines spawned with Go exit and
+// will return a *panics.Recovered if one of the child goroutines panics.
+func (h *WaitGroup) WaitAndRecover() *panics.Recovered {
+ h.wg.Wait()
+
+ // Return a recovered panic if we caught one from a child goroutine.
+ return h.pc.Recovered()
+}
diff --git a/vendor/github.com/spf13/afero/.gitignore b/vendor/github.com/spf13/afero/.gitignore
new file mode 100644
index 00000000..9c1d9861
--- /dev/null
+++ b/vendor/github.com/spf13/afero/.gitignore
@@ -0,0 +1,2 @@
+sftpfs/file1
+sftpfs/test/
diff --git a/vendor/github.com/spf13/afero/LICENSE.txt b/vendor/github.com/spf13/afero/LICENSE.txt
new file mode 100644
index 00000000..298f0e26
--- /dev/null
+++ b/vendor/github.com/spf13/afero/LICENSE.txt
@@ -0,0 +1,174 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
diff --git a/vendor/github.com/spf13/afero/README.md b/vendor/github.com/spf13/afero/README.md
new file mode 100644
index 00000000..3bafbfdf
--- /dev/null
+++ b/vendor/github.com/spf13/afero/README.md
@@ -0,0 +1,442 @@
+
+
+A FileSystem Abstraction System for Go
+
+[](https://github.com/spf13/afero/actions/workflows/test.yml) [](https://godoc.org/github.com/spf13/afero) [](https://gitter.im/spf13/afero?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+
+# Overview
+
+Afero is a filesystem framework providing a simple, uniform and universal API
+interacting with any filesystem, as an abstraction layer providing interfaces,
+types and methods. Afero has an exceptionally clean interface and simple design
+without needless constructors or initialization methods.
+
+Afero is also a library providing a base set of interoperable backend
+filesystems that make it easy to work with afero while retaining all the power
+and benefit of the os and ioutil packages.
+
+Afero provides significant improvements over using the os package alone, most
+notably the ability to create mock and testing filesystems without relying on the disk.
+
+It is suitable for use in any situation where you would consider using the OS
+package as it provides an additional abstraction that makes it easy to use a
+memory backed file system during testing. It also adds support for the http
+filesystem for full interoperability.
+
+
+## Afero Features
+
+* A single consistent API for accessing a variety of filesystems
+* Interoperation between a variety of file system types
+* A set of interfaces to encourage and enforce interoperability between backends
+* An atomic cross platform memory backed file system
+* Support for compositional (union) file systems by combining multiple file systems acting as one
+* Specialized backends which modify existing filesystems (Read Only, Regexp filtered)
+* A set of utility functions ported from io, ioutil & hugo to be afero aware
+* Wrapper for go 1.16 filesystem abstraction `io/fs.FS`
+
+# Using Afero
+
+Afero is easy to use and easier to adopt.
+
+A few different ways you could use Afero:
+
+* Use the interfaces alone to define your own file system.
+* Wrapper for the OS packages.
+* Define different filesystems for different parts of your application.
+* Use Afero for mock filesystems while testing
+
+## Step 1: Install Afero
+
+First use go get to install the latest version of the library.
+
+ $ go get github.com/spf13/afero
+
+Next include Afero in your application.
+```go
+import "github.com/spf13/afero"
+```
+
+## Step 2: Declare a backend
+
+First define a package variable and set it to a pointer to a filesystem.
+```go
+var AppFs = afero.NewMemMapFs()
+
+or
+
+var AppFs = afero.NewOsFs()
+```
+It is important to note that if you repeat the composite literal you
+will be using a completely new and isolated filesystem. In the case of
+OsFs it will still use the same underlying filesystem but will reduce
+the ability to drop in other filesystems as desired.
+
+## Step 3: Use it like you would the OS package
+
+Throughout your application use any function and method like you normally
+would.
+
+So if my application before had:
+```go
+os.Open("/tmp/foo")
+```
+We would replace it with:
+```go
+AppFs.Open("/tmp/foo")
+```
+
+`AppFs` being the variable we defined above.
+
+
+## List of all available functions
+
+File System Methods Available:
+```go
+Chmod(name string, mode os.FileMode) : error
+Chown(name string, uid, gid int) : error
+Chtimes(name string, atime time.Time, mtime time.Time) : error
+Create(name string) : File, error
+Mkdir(name string, perm os.FileMode) : error
+MkdirAll(path string, perm os.FileMode) : error
+Name() : string
+Open(name string) : File, error
+OpenFile(name string, flag int, perm os.FileMode) : File, error
+Remove(name string) : error
+RemoveAll(path string) : error
+Rename(oldname, newname string) : error
+Stat(name string) : os.FileInfo, error
+```
+File Interfaces and Methods Available:
+```go
+io.Closer
+io.Reader
+io.ReaderAt
+io.Seeker
+io.Writer
+io.WriterAt
+
+Name() : string
+Readdir(count int) : []os.FileInfo, error
+Readdirnames(n int) : []string, error
+Stat() : os.FileInfo, error
+Sync() : error
+Truncate(size int64) : error
+WriteString(s string) : ret int, err error
+```
+In some applications it may make sense to define a new package that
+simply exports the file system variable for easy access from anywhere.
+
+## Using Afero's utility functions
+
+Afero provides a set of functions to make it easier to use the underlying file systems.
+These functions have been primarily ported from io & ioutil with some developed for Hugo.
+
+The afero utilities support all afero compatible backends.
+
+The list of utilities includes:
+
+```go
+DirExists(path string) (bool, error)
+Exists(path string) (bool, error)
+FileContainsBytes(filename string, subslice []byte) (bool, error)
+GetTempDir(subPath string) string
+IsDir(path string) (bool, error)
+IsEmpty(path string) (bool, error)
+ReadDir(dirname string) ([]os.FileInfo, error)
+ReadFile(filename string) ([]byte, error)
+SafeWriteReader(path string, r io.Reader) (err error)
+TempDir(dir, prefix string) (name string, err error)
+TempFile(dir, prefix string) (f File, err error)
+Walk(root string, walkFn filepath.WalkFunc) error
+WriteFile(filename string, data []byte, perm os.FileMode) error
+WriteReader(path string, r io.Reader) (err error)
+```
+For a complete list see [Afero's GoDoc](https://godoc.org/github.com/spf13/afero)
+
+They are available under two different approaches to use. You can either call
+them directly where the first parameter of each function will be the file
+system, or you can declare a new `Afero`, a custom type used to bind these
+functions as methods to a given filesystem.
+
+### Calling utilities directly
+
+```go
+fs := new(afero.MemMapFs)
+f, err := afero.TempFile(fs,"", "ioutil-test")
+
+```
+
+### Calling via Afero
+
+```go
+fs := afero.NewMemMapFs()
+afs := &afero.Afero{Fs: fs}
+f, err := afs.TempFile("", "ioutil-test")
+```
+
+## Using Afero for Testing
+
+There is a large benefit to using a mock filesystem for testing. It has a
+completely blank state every time it is initialized and can be easily
+reproducible regardless of OS. You could create files to your heart’s content
+and the file access would be fast while also saving you from all the annoying
+issues with deleting temporary files, Windows file locking, etc. The MemMapFs
+backend is perfect for testing.
+
+* Much faster than performing I/O operations on disk
+* Avoid security issues and permissions
+* Far more control. 'rm -rf /' with confidence
+* Test setup is far more easier to do
+* No test cleanup needed
+
+One way to accomplish this is to define a variable as mentioned above.
+In your application this will be set to afero.NewOsFs() during testing you
+can set it to afero.NewMemMapFs().
+
+It wouldn't be uncommon to have each test initialize a blank slate memory
+backend. To do this I would define my `appFS = afero.NewOsFs()` somewhere
+appropriate in my application code. This approach ensures that Tests are order
+independent, with no test relying on the state left by an earlier test.
+
+Then in my tests I would initialize a new MemMapFs for each test:
+```go
+func TestExist(t *testing.T) {
+ appFS := afero.NewMemMapFs()
+ // create test files and directories
+ appFS.MkdirAll("src/a", 0755)
+ afero.WriteFile(appFS, "src/a/b", []byte("file b"), 0644)
+ afero.WriteFile(appFS, "src/c", []byte("file c"), 0644)
+ name := "src/c"
+ _, err := appFS.Stat(name)
+ if os.IsNotExist(err) {
+ t.Errorf("file \"%s\" does not exist.\n", name)
+ }
+}
+```
+
+# Available Backends
+
+## Operating System Native
+
+### OsFs
+
+The first is simply a wrapper around the native OS calls. This makes it
+very easy to use as all of the calls are the same as the existing OS
+calls. It also makes it trivial to have your code use the OS during
+operation and a mock filesystem during testing or as needed.
+
+```go
+appfs := afero.NewOsFs()
+appfs.MkdirAll("src/a", 0755)
+```
+
+## Memory Backed Storage
+
+### MemMapFs
+
+Afero also provides a fully atomic memory backed filesystem perfect for use in
+mocking and to speed up unnecessary disk io when persistence isn’t
+necessary. It is fully concurrent and will work within go routines
+safely.
+
+```go
+mm := afero.NewMemMapFs()
+mm.MkdirAll("src/a", 0755)
+```
+
+#### InMemoryFile
+
+As part of MemMapFs, Afero also provides an atomic, fully concurrent memory
+backed file implementation. This can be used in other memory backed file
+systems with ease. Plans are to add a radix tree memory stored file
+system using InMemoryFile.
+
+## Network Interfaces
+
+### SftpFs
+
+Afero has experimental support for secure file transfer protocol (sftp). Which can
+be used to perform file operations over a encrypted channel.
+
+### GCSFs
+
+Afero has experimental support for Google Cloud Storage (GCS). You can either set the
+`GOOGLE_APPLICATION_CREDENTIALS_JSON` env variable to your JSON credentials or use `opts` in
+`NewGcsFS` to configure access to your GCS bucket.
+
+Some known limitations of the existing implementation:
+* No Chmod support - The GCS ACL could probably be mapped to *nix style permissions but that would add another level of complexity and is ignored in this version.
+* No Chtimes support - Could be simulated with attributes (gcs a/m-times are set implicitly) but that's is left for another version.
+* Not thread safe - Also assumes all file operations are done through the same instance of the GcsFs. File operations between different GcsFs instances are not guaranteed to be consistent.
+
+
+## Filtering Backends
+
+### BasePathFs
+
+The BasePathFs restricts all operations to a given path within an Fs.
+The given file name to the operations on this Fs will be prepended with
+the base path before calling the source Fs.
+
+```go
+bp := afero.NewBasePathFs(afero.NewOsFs(), "/base/path")
+```
+
+### ReadOnlyFs
+
+A thin wrapper around the source Fs providing a read only view.
+
+```go
+fs := afero.NewReadOnlyFs(afero.NewOsFs())
+_, err := fs.Create("/file.txt")
+// err = syscall.EPERM
+```
+
+# RegexpFs
+
+A filtered view on file names, any file NOT matching
+the passed regexp will be treated as non-existing.
+Files not matching the regexp provided will not be created.
+Directories are not filtered.
+
+```go
+fs := afero.NewRegexpFs(afero.NewMemMapFs(), regexp.MustCompile(`\.txt$`))
+_, err := fs.Create("/file.html")
+// err = syscall.ENOENT
+```
+
+### HttpFs
+
+Afero provides an http compatible backend which can wrap any of the existing
+backends.
+
+The Http package requires a slightly specific version of Open which
+returns an http.File type.
+
+Afero provides an httpFs file system which satisfies this requirement.
+Any Afero FileSystem can be used as an httpFs.
+
+```go
+httpFs := afero.NewHttpFs()
+fileserver := http.FileServer(httpFs.Dir())
+http.Handle("/", fileserver)
+```
+
+## Composite Backends
+
+Afero provides the ability have two filesystems (or more) act as a single
+file system.
+
+### CacheOnReadFs
+
+The CacheOnReadFs will lazily make copies of any accessed files from the base
+layer into the overlay. Subsequent reads will be pulled from the overlay
+directly permitting the request is within the cache duration of when it was
+created in the overlay.
+
+If the base filesystem is writeable, any changes to files will be
+done first to the base, then to the overlay layer. Write calls to open file
+handles like `Write()` or `Truncate()` to the overlay first.
+
+To writing files to the overlay only, you can use the overlay Fs directly (not
+via the union Fs).
+
+Cache files in the layer for the given time.Duration, a cache duration of 0
+means "forever" meaning the file will not be re-requested from the base ever.
+
+A read-only base will make the overlay also read-only but still copy files
+from the base to the overlay when they're not present (or outdated) in the
+caching layer.
+
+```go
+base := afero.NewOsFs()
+layer := afero.NewMemMapFs()
+ufs := afero.NewCacheOnReadFs(base, layer, 100 * time.Second)
+```
+
+### CopyOnWriteFs()
+
+The CopyOnWriteFs is a read only base file system with a potentially
+writeable layer on top.
+
+Read operations will first look in the overlay and if not found there, will
+serve the file from the base.
+
+Changes to the file system will only be made in the overlay.
+
+Any attempt to modify a file found only in the base will copy the file to the
+overlay layer before modification (including opening a file with a writable
+handle).
+
+Removing and Renaming files present only in the base layer is not currently
+permitted. If a file is present in the base layer and the overlay, only the
+overlay will be removed/renamed.
+
+```go
+ base := afero.NewOsFs()
+ roBase := afero.NewReadOnlyFs(base)
+ ufs := afero.NewCopyOnWriteFs(roBase, afero.NewMemMapFs())
+
+ fh, _ = ufs.Create("/home/test/file2.txt")
+ fh.WriteString("This is a test")
+ fh.Close()
+```
+
+In this example all write operations will only occur in memory (MemMapFs)
+leaving the base filesystem (OsFs) untouched.
+
+
+## Desired/possible backends
+
+The following is a short list of possible backends we hope someone will
+implement:
+
+* SSH
+* S3
+
+# About the project
+
+## What's in the name
+
+Afero comes from the latin roots Ad-Facere.
+
+**"Ad"** is a prefix meaning "to".
+
+**"Facere"** is a form of the root "faciō" making "make or do".
+
+The literal meaning of afero is "to make" or "to do" which seems very fitting
+for a library that allows one to make files and directories and do things with them.
+
+The English word that shares the same roots as Afero is "affair". Affair shares
+the same concept but as a noun it means "something that is made or done" or "an
+object of a particular type".
+
+It's also nice that unlike some of my other libraries (hugo, cobra, viper) it
+Googles very well.
+
+## Release Notes
+
+See the [Releases Page](https://github.com/spf13/afero/releases).
+
+## Contributing
+
+1. Fork it
+2. Create your feature branch (`git checkout -b my-new-feature`)
+3. Commit your changes (`git commit -am 'Add some feature'`)
+4. Push to the branch (`git push origin my-new-feature`)
+5. Create new Pull Request
+
+## Contributors
+
+Names in no particular order:
+
+* [spf13](https://github.com/spf13)
+* [jaqx0r](https://github.com/jaqx0r)
+* [mbertschler](https://github.com/mbertschler)
+* [xor-gate](https://github.com/xor-gate)
+
+## License
+
+Afero is released under the Apache 2.0 license. See
+[LICENSE.txt](https://github.com/spf13/afero/blob/master/LICENSE.txt)
diff --git a/vendor/github.com/spf13/afero/afero.go b/vendor/github.com/spf13/afero/afero.go
new file mode 100644
index 00000000..39f65852
--- /dev/null
+++ b/vendor/github.com/spf13/afero/afero.go
@@ -0,0 +1,111 @@
+// Copyright © 2014 Steve Francia .
+// Copyright 2013 tsuru authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package afero provides types and methods for interacting with the filesystem,
+// as an abstraction layer.
+
+// Afero also provides a few implementations that are mostly interoperable. One that
+// uses the operating system filesystem, one that uses memory to store files
+// (cross platform) and an interface that should be implemented if you want to
+// provide your own filesystem.
+
+package afero
+
+import (
+ "errors"
+ "io"
+ "os"
+ "time"
+)
+
+type Afero struct {
+ Fs
+}
+
+// File represents a file in the filesystem.
+type File interface {
+ io.Closer
+ io.Reader
+ io.ReaderAt
+ io.Seeker
+ io.Writer
+ io.WriterAt
+
+ Name() string
+ Readdir(count int) ([]os.FileInfo, error)
+ Readdirnames(n int) ([]string, error)
+ Stat() (os.FileInfo, error)
+ Sync() error
+ Truncate(size int64) error
+ WriteString(s string) (ret int, err error)
+}
+
+// Fs is the filesystem interface.
+//
+// Any simulated or real filesystem should implement this interface.
+type Fs interface {
+ // Create creates a file in the filesystem, returning the file and an
+ // error, if any happens.
+ Create(name string) (File, error)
+
+ // Mkdir creates a directory in the filesystem, return an error if any
+ // happens.
+ Mkdir(name string, perm os.FileMode) error
+
+ // MkdirAll creates a directory path and all parents that does not exist
+ // yet.
+ MkdirAll(path string, perm os.FileMode) error
+
+ // Open opens a file, returning it or an error, if any happens.
+ Open(name string) (File, error)
+
+ // OpenFile opens a file using the given flags and the given mode.
+ OpenFile(name string, flag int, perm os.FileMode) (File, error)
+
+ // Remove removes a file identified by name, returning an error, if any
+ // happens.
+ Remove(name string) error
+
+ // RemoveAll removes a directory path and any children it contains. It
+ // does not fail if the path does not exist (return nil).
+ RemoveAll(path string) error
+
+ // Rename renames a file.
+ Rename(oldname, newname string) error
+
+ // Stat returns a FileInfo describing the named file, or an error, if any
+ // happens.
+ Stat(name string) (os.FileInfo, error)
+
+ // The name of this FileSystem
+ Name() string
+
+ // Chmod changes the mode of the named file to mode.
+ Chmod(name string, mode os.FileMode) error
+
+ // Chown changes the uid and gid of the named file.
+ Chown(name string, uid, gid int) error
+
+ // Chtimes changes the access and modification times of the named file
+ Chtimes(name string, atime time.Time, mtime time.Time) error
+}
+
+var (
+ ErrFileClosed = errors.New("File is closed")
+ ErrOutOfRange = errors.New("out of range")
+ ErrTooLarge = errors.New("too large")
+ ErrFileNotFound = os.ErrNotExist
+ ErrFileExists = os.ErrExist
+ ErrDestinationExists = os.ErrExist
+)
diff --git a/vendor/github.com/spf13/afero/appveyor.yml b/vendor/github.com/spf13/afero/appveyor.yml
new file mode 100644
index 00000000..65e20e8c
--- /dev/null
+++ b/vendor/github.com/spf13/afero/appveyor.yml
@@ -0,0 +1,10 @@
+# This currently does nothing. We have moved to GitHub action, but this is kept
+# until spf13 has disabled this project in AppVeyor.
+version: '{build}'
+clone_folder: C:\gopath\src\github.com\spf13\afero
+environment:
+ GOPATH: C:\gopath
+build_script:
+- cmd: >-
+ go version
+
diff --git a/vendor/github.com/spf13/afero/basepath.go b/vendor/github.com/spf13/afero/basepath.go
new file mode 100644
index 00000000..2e72793a
--- /dev/null
+++ b/vendor/github.com/spf13/afero/basepath.go
@@ -0,0 +1,222 @@
+package afero
+
+import (
+ "io/fs"
+ "os"
+ "path/filepath"
+ "runtime"
+ "strings"
+ "time"
+)
+
+var (
+ _ Lstater = (*BasePathFs)(nil)
+ _ fs.ReadDirFile = (*BasePathFile)(nil)
+)
+
+// The BasePathFs restricts all operations to a given path within an Fs.
+// The given file name to the operations on this Fs will be prepended with
+// the base path before calling the base Fs.
+// Any file name (after filepath.Clean()) outside this base path will be
+// treated as non existing file.
+//
+// Note that it does not clean the error messages on return, so you may
+// reveal the real path on errors.
+type BasePathFs struct {
+ source Fs
+ path string
+}
+
+type BasePathFile struct {
+ File
+ path string
+}
+
+func (f *BasePathFile) Name() string {
+ sourcename := f.File.Name()
+ return strings.TrimPrefix(sourcename, filepath.Clean(f.path))
+}
+
+func (f *BasePathFile) ReadDir(n int) ([]fs.DirEntry, error) {
+ if rdf, ok := f.File.(fs.ReadDirFile); ok {
+ return rdf.ReadDir(n)
+ }
+ return readDirFile{f.File}.ReadDir(n)
+}
+
+func NewBasePathFs(source Fs, path string) Fs {
+ return &BasePathFs{source: source, path: path}
+}
+
+// on a file outside the base path it returns the given file name and an error,
+// else the given file with the base path prepended
+func (b *BasePathFs) RealPath(name string) (path string, err error) {
+ if err := validateBasePathName(name); err != nil {
+ return name, err
+ }
+
+ bpath := filepath.Clean(b.path)
+ path = filepath.Clean(filepath.Join(bpath, name))
+ if !strings.HasPrefix(path, bpath) {
+ return name, os.ErrNotExist
+ }
+
+ return path, nil
+}
+
+func validateBasePathName(name string) error {
+ if runtime.GOOS != "windows" {
+ // Not much to do here;
+ // the virtual file paths all look absolute on *nix.
+ return nil
+ }
+
+ // On Windows a common mistake would be to provide an absolute OS path
+ // We could strip out the base part, but that would not be very portable.
+ if filepath.IsAbs(name) {
+ return os.ErrNotExist
+ }
+
+ return nil
+}
+
+func (b *BasePathFs) Chtimes(name string, atime, mtime time.Time) (err error) {
+ if name, err = b.RealPath(name); err != nil {
+ return &os.PathError{Op: "chtimes", Path: name, Err: err}
+ }
+ return b.source.Chtimes(name, atime, mtime)
+}
+
+func (b *BasePathFs) Chmod(name string, mode os.FileMode) (err error) {
+ if name, err = b.RealPath(name); err != nil {
+ return &os.PathError{Op: "chmod", Path: name, Err: err}
+ }
+ return b.source.Chmod(name, mode)
+}
+
+func (b *BasePathFs) Chown(name string, uid, gid int) (err error) {
+ if name, err = b.RealPath(name); err != nil {
+ return &os.PathError{Op: "chown", Path: name, Err: err}
+ }
+ return b.source.Chown(name, uid, gid)
+}
+
+func (b *BasePathFs) Name() string {
+ return "BasePathFs"
+}
+
+func (b *BasePathFs) Stat(name string) (fi os.FileInfo, err error) {
+ if name, err = b.RealPath(name); err != nil {
+ return nil, &os.PathError{Op: "stat", Path: name, Err: err}
+ }
+ return b.source.Stat(name)
+}
+
+func (b *BasePathFs) Rename(oldname, newname string) (err error) {
+ if oldname, err = b.RealPath(oldname); err != nil {
+ return &os.PathError{Op: "rename", Path: oldname, Err: err}
+ }
+ if newname, err = b.RealPath(newname); err != nil {
+ return &os.PathError{Op: "rename", Path: newname, Err: err}
+ }
+ return b.source.Rename(oldname, newname)
+}
+
+func (b *BasePathFs) RemoveAll(name string) (err error) {
+ if name, err = b.RealPath(name); err != nil {
+ return &os.PathError{Op: "remove_all", Path: name, Err: err}
+ }
+ return b.source.RemoveAll(name)
+}
+
+func (b *BasePathFs) Remove(name string) (err error) {
+ if name, err = b.RealPath(name); err != nil {
+ return &os.PathError{Op: "remove", Path: name, Err: err}
+ }
+ return b.source.Remove(name)
+}
+
+func (b *BasePathFs) OpenFile(name string, flag int, mode os.FileMode) (f File, err error) {
+ if name, err = b.RealPath(name); err != nil {
+ return nil, &os.PathError{Op: "openfile", Path: name, Err: err}
+ }
+ sourcef, err := b.source.OpenFile(name, flag, mode)
+ if err != nil {
+ return nil, err
+ }
+ return &BasePathFile{sourcef, b.path}, nil
+}
+
+func (b *BasePathFs) Open(name string) (f File, err error) {
+ if name, err = b.RealPath(name); err != nil {
+ return nil, &os.PathError{Op: "open", Path: name, Err: err}
+ }
+ sourcef, err := b.source.Open(name)
+ if err != nil {
+ return nil, err
+ }
+ return &BasePathFile{File: sourcef, path: b.path}, nil
+}
+
+func (b *BasePathFs) Mkdir(name string, mode os.FileMode) (err error) {
+ if name, err = b.RealPath(name); err != nil {
+ return &os.PathError{Op: "mkdir", Path: name, Err: err}
+ }
+ return b.source.Mkdir(name, mode)
+}
+
+func (b *BasePathFs) MkdirAll(name string, mode os.FileMode) (err error) {
+ if name, err = b.RealPath(name); err != nil {
+ return &os.PathError{Op: "mkdir", Path: name, Err: err}
+ }
+ return b.source.MkdirAll(name, mode)
+}
+
+func (b *BasePathFs) Create(name string) (f File, err error) {
+ if name, err = b.RealPath(name); err != nil {
+ return nil, &os.PathError{Op: "create", Path: name, Err: err}
+ }
+ sourcef, err := b.source.Create(name)
+ if err != nil {
+ return nil, err
+ }
+ return &BasePathFile{File: sourcef, path: b.path}, nil
+}
+
+func (b *BasePathFs) LstatIfPossible(name string) (os.FileInfo, bool, error) {
+ name, err := b.RealPath(name)
+ if err != nil {
+ return nil, false, &os.PathError{Op: "lstat", Path: name, Err: err}
+ }
+ if lstater, ok := b.source.(Lstater); ok {
+ return lstater.LstatIfPossible(name)
+ }
+ fi, err := b.source.Stat(name)
+ return fi, false, err
+}
+
+func (b *BasePathFs) SymlinkIfPossible(oldname, newname string) error {
+ oldname, err := b.RealPath(oldname)
+ if err != nil {
+ return &os.LinkError{Op: "symlink", Old: oldname, New: newname, Err: err}
+ }
+ newname, err = b.RealPath(newname)
+ if err != nil {
+ return &os.LinkError{Op: "symlink", Old: oldname, New: newname, Err: err}
+ }
+ if linker, ok := b.source.(Linker); ok {
+ return linker.SymlinkIfPossible(oldname, newname)
+ }
+ return &os.LinkError{Op: "symlink", Old: oldname, New: newname, Err: ErrNoSymlink}
+}
+
+func (b *BasePathFs) ReadlinkIfPossible(name string) (string, error) {
+ name, err := b.RealPath(name)
+ if err != nil {
+ return "", &os.PathError{Op: "readlink", Path: name, Err: err}
+ }
+ if reader, ok := b.source.(LinkReader); ok {
+ return reader.ReadlinkIfPossible(name)
+ }
+ return "", &os.PathError{Op: "readlink", Path: name, Err: ErrNoReadlink}
+}
diff --git a/vendor/github.com/spf13/afero/cacheOnReadFs.go b/vendor/github.com/spf13/afero/cacheOnReadFs.go
new file mode 100644
index 00000000..017d344f
--- /dev/null
+++ b/vendor/github.com/spf13/afero/cacheOnReadFs.go
@@ -0,0 +1,315 @@
+package afero
+
+import (
+ "os"
+ "syscall"
+ "time"
+)
+
+// If the cache duration is 0, cache time will be unlimited, i.e. once
+// a file is in the layer, the base will never be read again for this file.
+//
+// For cache times greater than 0, the modification time of a file is
+// checked. Note that a lot of file system implementations only allow a
+// resolution of a second for timestamps... or as the godoc for os.Chtimes()
+// states: "The underlying filesystem may truncate or round the values to a
+// less precise time unit."
+//
+// This caching union will forward all write calls also to the base file
+// system first. To prevent writing to the base Fs, wrap it in a read-only
+// filter - Note: this will also make the overlay read-only, for writing files
+// in the overlay, use the overlay Fs directly, not via the union Fs.
+type CacheOnReadFs struct {
+ base Fs
+ layer Fs
+ cacheTime time.Duration
+}
+
+func NewCacheOnReadFs(base Fs, layer Fs, cacheTime time.Duration) Fs {
+ return &CacheOnReadFs{base: base, layer: layer, cacheTime: cacheTime}
+}
+
+type cacheState int
+
+const (
+ // not present in the overlay, unknown if it exists in the base:
+ cacheMiss cacheState = iota
+ // present in the overlay and in base, base file is newer:
+ cacheStale
+ // present in the overlay - with cache time == 0 it may exist in the base,
+ // with cacheTime > 0 it exists in the base and is same age or newer in the
+ // overlay
+ cacheHit
+ // happens if someone writes directly to the overlay without
+ // going through this union
+ cacheLocal
+)
+
+func (u *CacheOnReadFs) cacheStatus(name string) (state cacheState, fi os.FileInfo, err error) {
+ var lfi, bfi os.FileInfo
+ lfi, err = u.layer.Stat(name)
+ if err == nil {
+ if u.cacheTime == 0 {
+ return cacheHit, lfi, nil
+ }
+ if lfi.ModTime().Add(u.cacheTime).Before(time.Now()) {
+ bfi, err = u.base.Stat(name)
+ if err != nil {
+ return cacheLocal, lfi, nil
+ }
+ if bfi.ModTime().After(lfi.ModTime()) {
+ return cacheStale, bfi, nil
+ }
+ }
+ return cacheHit, lfi, nil
+ }
+
+ if err == syscall.ENOENT || os.IsNotExist(err) {
+ return cacheMiss, nil, nil
+ }
+
+ return cacheMiss, nil, err
+}
+
+func (u *CacheOnReadFs) copyToLayer(name string) error {
+ return copyToLayer(u.base, u.layer, name)
+}
+
+func (u *CacheOnReadFs) copyFileToLayer(name string, flag int, perm os.FileMode) error {
+ return copyFileToLayer(u.base, u.layer, name, flag, perm)
+}
+
+func (u *CacheOnReadFs) Chtimes(name string, atime, mtime time.Time) error {
+ st, _, err := u.cacheStatus(name)
+ if err != nil {
+ return err
+ }
+ switch st {
+ case cacheLocal:
+ case cacheHit:
+ err = u.base.Chtimes(name, atime, mtime)
+ case cacheStale, cacheMiss:
+ if err := u.copyToLayer(name); err != nil {
+ return err
+ }
+ err = u.base.Chtimes(name, atime, mtime)
+ }
+ if err != nil {
+ return err
+ }
+ return u.layer.Chtimes(name, atime, mtime)
+}
+
+func (u *CacheOnReadFs) Chmod(name string, mode os.FileMode) error {
+ st, _, err := u.cacheStatus(name)
+ if err != nil {
+ return err
+ }
+ switch st {
+ case cacheLocal:
+ case cacheHit:
+ err = u.base.Chmod(name, mode)
+ case cacheStale, cacheMiss:
+ if err := u.copyToLayer(name); err != nil {
+ return err
+ }
+ err = u.base.Chmod(name, mode)
+ }
+ if err != nil {
+ return err
+ }
+ return u.layer.Chmod(name, mode)
+}
+
+func (u *CacheOnReadFs) Chown(name string, uid, gid int) error {
+ st, _, err := u.cacheStatus(name)
+ if err != nil {
+ return err
+ }
+ switch st {
+ case cacheLocal:
+ case cacheHit:
+ err = u.base.Chown(name, uid, gid)
+ case cacheStale, cacheMiss:
+ if err := u.copyToLayer(name); err != nil {
+ return err
+ }
+ err = u.base.Chown(name, uid, gid)
+ }
+ if err != nil {
+ return err
+ }
+ return u.layer.Chown(name, uid, gid)
+}
+
+func (u *CacheOnReadFs) Stat(name string) (os.FileInfo, error) {
+ st, fi, err := u.cacheStatus(name)
+ if err != nil {
+ return nil, err
+ }
+ switch st {
+ case cacheMiss:
+ return u.base.Stat(name)
+ default: // cacheStale has base, cacheHit and cacheLocal the layer os.FileInfo
+ return fi, nil
+ }
+}
+
+func (u *CacheOnReadFs) Rename(oldname, newname string) error {
+ st, _, err := u.cacheStatus(oldname)
+ if err != nil {
+ return err
+ }
+ switch st {
+ case cacheLocal:
+ case cacheHit:
+ err = u.base.Rename(oldname, newname)
+ case cacheStale, cacheMiss:
+ if err := u.copyToLayer(oldname); err != nil {
+ return err
+ }
+ err = u.base.Rename(oldname, newname)
+ }
+ if err != nil {
+ return err
+ }
+ return u.layer.Rename(oldname, newname)
+}
+
+func (u *CacheOnReadFs) Remove(name string) error {
+ st, _, err := u.cacheStatus(name)
+ if err != nil {
+ return err
+ }
+ switch st {
+ case cacheLocal:
+ case cacheHit, cacheStale, cacheMiss:
+ err = u.base.Remove(name)
+ }
+ if err != nil {
+ return err
+ }
+ return u.layer.Remove(name)
+}
+
+func (u *CacheOnReadFs) RemoveAll(name string) error {
+ st, _, err := u.cacheStatus(name)
+ if err != nil {
+ return err
+ }
+ switch st {
+ case cacheLocal:
+ case cacheHit, cacheStale, cacheMiss:
+ err = u.base.RemoveAll(name)
+ }
+ if err != nil {
+ return err
+ }
+ return u.layer.RemoveAll(name)
+}
+
+func (u *CacheOnReadFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
+ st, _, err := u.cacheStatus(name)
+ if err != nil {
+ return nil, err
+ }
+ switch st {
+ case cacheLocal, cacheHit:
+ default:
+ if err := u.copyFileToLayer(name, flag, perm); err != nil {
+ return nil, err
+ }
+ }
+ if flag&(os.O_WRONLY|syscall.O_RDWR|os.O_APPEND|os.O_CREATE|os.O_TRUNC) != 0 {
+ bfi, err := u.base.OpenFile(name, flag, perm)
+ if err != nil {
+ return nil, err
+ }
+ lfi, err := u.layer.OpenFile(name, flag, perm)
+ if err != nil {
+ bfi.Close() // oops, what if O_TRUNC was set and file opening in the layer failed...?
+ return nil, err
+ }
+ return &UnionFile{Base: bfi, Layer: lfi}, nil
+ }
+ return u.layer.OpenFile(name, flag, perm)
+}
+
+func (u *CacheOnReadFs) Open(name string) (File, error) {
+ st, fi, err := u.cacheStatus(name)
+ if err != nil {
+ return nil, err
+ }
+
+ switch st {
+ case cacheLocal:
+ return u.layer.Open(name)
+
+ case cacheMiss:
+ bfi, err := u.base.Stat(name)
+ if err != nil {
+ return nil, err
+ }
+ if bfi.IsDir() {
+ return u.base.Open(name)
+ }
+ if err := u.copyToLayer(name); err != nil {
+ return nil, err
+ }
+ return u.layer.Open(name)
+
+ case cacheStale:
+ if !fi.IsDir() {
+ if err := u.copyToLayer(name); err != nil {
+ return nil, err
+ }
+ return u.layer.Open(name)
+ }
+ case cacheHit:
+ if !fi.IsDir() {
+ return u.layer.Open(name)
+ }
+ }
+ // the dirs from cacheHit, cacheStale fall down here:
+ bfile, _ := u.base.Open(name)
+ lfile, err := u.layer.Open(name)
+ if err != nil && bfile == nil {
+ return nil, err
+ }
+ return &UnionFile{Base: bfile, Layer: lfile}, nil
+}
+
+func (u *CacheOnReadFs) Mkdir(name string, perm os.FileMode) error {
+ err := u.base.Mkdir(name, perm)
+ if err != nil {
+ return err
+ }
+ return u.layer.MkdirAll(name, perm) // yes, MkdirAll... we cannot assume it exists in the cache
+}
+
+func (u *CacheOnReadFs) Name() string {
+ return "CacheOnReadFs"
+}
+
+func (u *CacheOnReadFs) MkdirAll(name string, perm os.FileMode) error {
+ err := u.base.MkdirAll(name, perm)
+ if err != nil {
+ return err
+ }
+ return u.layer.MkdirAll(name, perm)
+}
+
+func (u *CacheOnReadFs) Create(name string) (File, error) {
+ bfh, err := u.base.Create(name)
+ if err != nil {
+ return nil, err
+ }
+ lfh, err := u.layer.Create(name)
+ if err != nil {
+ // oops, see comment about OS_TRUNC above, should we remove? then we have to
+ // remember if the file did not exist before
+ bfh.Close()
+ return nil, err
+ }
+ return &UnionFile{Base: bfh, Layer: lfh}, nil
+}
diff --git a/vendor/github.com/spf13/afero/const_bsds.go b/vendor/github.com/spf13/afero/const_bsds.go
new file mode 100644
index 00000000..30855de5
--- /dev/null
+++ b/vendor/github.com/spf13/afero/const_bsds.go
@@ -0,0 +1,23 @@
+// Copyright © 2016 Steve Francia .
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//go:build aix || darwin || openbsd || freebsd || netbsd || dragonfly || zos
+// +build aix darwin openbsd freebsd netbsd dragonfly zos
+
+package afero
+
+import (
+ "syscall"
+)
+
+const BADFD = syscall.EBADF
diff --git a/vendor/github.com/spf13/afero/const_win_unix.go b/vendor/github.com/spf13/afero/const_win_unix.go
new file mode 100644
index 00000000..12792d21
--- /dev/null
+++ b/vendor/github.com/spf13/afero/const_win_unix.go
@@ -0,0 +1,22 @@
+// Copyright © 2016 Steve Francia .
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//go:build !darwin && !openbsd && !freebsd && !dragonfly && !netbsd && !aix && !zos
+// +build !darwin,!openbsd,!freebsd,!dragonfly,!netbsd,!aix,!zos
+
+package afero
+
+import (
+ "syscall"
+)
+
+const BADFD = syscall.EBADFD
diff --git a/vendor/github.com/spf13/afero/copyOnWriteFs.go b/vendor/github.com/spf13/afero/copyOnWriteFs.go
new file mode 100644
index 00000000..184d6dd7
--- /dev/null
+++ b/vendor/github.com/spf13/afero/copyOnWriteFs.go
@@ -0,0 +1,327 @@
+package afero
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+ "syscall"
+ "time"
+)
+
+var _ Lstater = (*CopyOnWriteFs)(nil)
+
+// The CopyOnWriteFs is a union filesystem: a read only base file system with
+// a possibly writeable layer on top. Changes to the file system will only
+// be made in the overlay: Changing an existing file in the base layer which
+// is not present in the overlay will copy the file to the overlay ("changing"
+// includes also calls to e.g. Chtimes(), Chmod() and Chown()).
+//
+// Reading directories is currently only supported via Open(), not OpenFile().
+type CopyOnWriteFs struct {
+ base Fs
+ layer Fs
+}
+
+func NewCopyOnWriteFs(base Fs, layer Fs) Fs {
+ return &CopyOnWriteFs{base: base, layer: layer}
+}
+
+// Returns true if the file is not in the overlay
+func (u *CopyOnWriteFs) isBaseFile(name string) (bool, error) {
+ if _, err := u.layer.Stat(name); err == nil {
+ return false, nil
+ }
+ _, err := u.base.Stat(name)
+ if err != nil {
+ if oerr, ok := err.(*os.PathError); ok {
+ if oerr.Err == os.ErrNotExist || oerr.Err == syscall.ENOENT || oerr.Err == syscall.ENOTDIR {
+ return false, nil
+ }
+ }
+ if err == syscall.ENOENT {
+ return false, nil
+ }
+ }
+ return true, err
+}
+
+func (u *CopyOnWriteFs) copyToLayer(name string) error {
+ return copyToLayer(u.base, u.layer, name)
+}
+
+func (u *CopyOnWriteFs) Chtimes(name string, atime, mtime time.Time) error {
+ b, err := u.isBaseFile(name)
+ if err != nil {
+ return err
+ }
+ if b {
+ if err := u.copyToLayer(name); err != nil {
+ return err
+ }
+ }
+ return u.layer.Chtimes(name, atime, mtime)
+}
+
+func (u *CopyOnWriteFs) Chmod(name string, mode os.FileMode) error {
+ b, err := u.isBaseFile(name)
+ if err != nil {
+ return err
+ }
+ if b {
+ if err := u.copyToLayer(name); err != nil {
+ return err
+ }
+ }
+ return u.layer.Chmod(name, mode)
+}
+
+func (u *CopyOnWriteFs) Chown(name string, uid, gid int) error {
+ b, err := u.isBaseFile(name)
+ if err != nil {
+ return err
+ }
+ if b {
+ if err := u.copyToLayer(name); err != nil {
+ return err
+ }
+ }
+ return u.layer.Chown(name, uid, gid)
+}
+
+func (u *CopyOnWriteFs) Stat(name string) (os.FileInfo, error) {
+ fi, err := u.layer.Stat(name)
+ if err != nil {
+ isNotExist := u.isNotExist(err)
+ if isNotExist {
+ return u.base.Stat(name)
+ }
+ return nil, err
+ }
+ return fi, nil
+}
+
+func (u *CopyOnWriteFs) LstatIfPossible(name string) (os.FileInfo, bool, error) {
+ llayer, ok1 := u.layer.(Lstater)
+ lbase, ok2 := u.base.(Lstater)
+
+ if ok1 {
+ fi, b, err := llayer.LstatIfPossible(name)
+ if err == nil {
+ return fi, b, nil
+ }
+
+ if !u.isNotExist(err) {
+ return nil, b, err
+ }
+ }
+
+ if ok2 {
+ fi, b, err := lbase.LstatIfPossible(name)
+ if err == nil {
+ return fi, b, nil
+ }
+ if !u.isNotExist(err) {
+ return nil, b, err
+ }
+ }
+
+ fi, err := u.Stat(name)
+
+ return fi, false, err
+}
+
+func (u *CopyOnWriteFs) SymlinkIfPossible(oldname, newname string) error {
+ if slayer, ok := u.layer.(Linker); ok {
+ return slayer.SymlinkIfPossible(oldname, newname)
+ }
+
+ return &os.LinkError{Op: "symlink", Old: oldname, New: newname, Err: ErrNoSymlink}
+}
+
+func (u *CopyOnWriteFs) ReadlinkIfPossible(name string) (string, error) {
+ if rlayer, ok := u.layer.(LinkReader); ok {
+ return rlayer.ReadlinkIfPossible(name)
+ }
+
+ if rbase, ok := u.base.(LinkReader); ok {
+ return rbase.ReadlinkIfPossible(name)
+ }
+
+ return "", &os.PathError{Op: "readlink", Path: name, Err: ErrNoReadlink}
+}
+
+func (u *CopyOnWriteFs) isNotExist(err error) bool {
+ if e, ok := err.(*os.PathError); ok {
+ err = e.Err
+ }
+ if err == os.ErrNotExist || err == syscall.ENOENT || err == syscall.ENOTDIR {
+ return true
+ }
+ return false
+}
+
+// Renaming files present only in the base layer is not permitted
+func (u *CopyOnWriteFs) Rename(oldname, newname string) error {
+ b, err := u.isBaseFile(oldname)
+ if err != nil {
+ return err
+ }
+ if b {
+ return syscall.EPERM
+ }
+ return u.layer.Rename(oldname, newname)
+}
+
+// Removing files present only in the base layer is not permitted. If
+// a file is present in the base layer and the overlay, only the overlay
+// will be removed.
+func (u *CopyOnWriteFs) Remove(name string) error {
+ err := u.layer.Remove(name)
+ switch err {
+ case syscall.ENOENT:
+ _, err = u.base.Stat(name)
+ if err == nil {
+ return syscall.EPERM
+ }
+ return syscall.ENOENT
+ default:
+ return err
+ }
+}
+
+func (u *CopyOnWriteFs) RemoveAll(name string) error {
+ err := u.layer.RemoveAll(name)
+ switch err {
+ case syscall.ENOENT:
+ _, err = u.base.Stat(name)
+ if err == nil {
+ return syscall.EPERM
+ }
+ return syscall.ENOENT
+ default:
+ return err
+ }
+}
+
+func (u *CopyOnWriteFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
+ b, err := u.isBaseFile(name)
+ if err != nil {
+ return nil, err
+ }
+
+ if flag&(os.O_WRONLY|os.O_RDWR|os.O_APPEND|os.O_CREATE|os.O_TRUNC) != 0 {
+ if b {
+ if err = u.copyToLayer(name); err != nil {
+ return nil, err
+ }
+ return u.layer.OpenFile(name, flag, perm)
+ }
+
+ dir := filepath.Dir(name)
+ isaDir, err := IsDir(u.base, dir)
+ if err != nil && !os.IsNotExist(err) {
+ return nil, err
+ }
+ if isaDir {
+ if err = u.layer.MkdirAll(dir, 0o777); err != nil {
+ return nil, err
+ }
+ return u.layer.OpenFile(name, flag, perm)
+ }
+
+ isaDir, err = IsDir(u.layer, dir)
+ if err != nil {
+ return nil, err
+ }
+ if isaDir {
+ return u.layer.OpenFile(name, flag, perm)
+ }
+
+ return nil, &os.PathError{Op: "open", Path: name, Err: syscall.ENOTDIR} // ...or os.ErrNotExist?
+ }
+ if b {
+ return u.base.OpenFile(name, flag, perm)
+ }
+ return u.layer.OpenFile(name, flag, perm)
+}
+
+// This function handles the 9 different possibilities caused
+// by the union which are the intersection of the following...
+//
+// layer: doesn't exist, exists as a file, and exists as a directory
+// base: doesn't exist, exists as a file, and exists as a directory
+func (u *CopyOnWriteFs) Open(name string) (File, error) {
+ // Since the overlay overrides the base we check that first
+ b, err := u.isBaseFile(name)
+ if err != nil {
+ return nil, err
+ }
+
+ // If overlay doesn't exist, return the base (base state irrelevant)
+ if b {
+ return u.base.Open(name)
+ }
+
+ // If overlay is a file, return it (base state irrelevant)
+ dir, err := IsDir(u.layer, name)
+ if err != nil {
+ return nil, err
+ }
+ if !dir {
+ return u.layer.Open(name)
+ }
+
+ // Overlay is a directory, base state now matters.
+ // Base state has 3 states to check but 2 outcomes:
+ // A. It's a file or non-readable in the base (return just the overlay)
+ // B. It's an accessible directory in the base (return a UnionFile)
+
+ // If base is file or nonreadable, return overlay
+ dir, err = IsDir(u.base, name)
+ if !dir || err != nil {
+ return u.layer.Open(name)
+ }
+
+ // Both base & layer are directories
+ // Return union file (if opens are without error)
+ bfile, bErr := u.base.Open(name)
+ lfile, lErr := u.layer.Open(name)
+
+ // If either have errors at this point something is very wrong. Return nil and the errors
+ if bErr != nil || lErr != nil {
+ return nil, fmt.Errorf("BaseErr: %v\nOverlayErr: %v", bErr, lErr)
+ }
+
+ return &UnionFile{Base: bfile, Layer: lfile}, nil
+}
+
+func (u *CopyOnWriteFs) Mkdir(name string, perm os.FileMode) error {
+ dir, err := IsDir(u.base, name)
+ if err != nil {
+ return u.layer.MkdirAll(name, perm)
+ }
+ if dir {
+ return ErrFileExists
+ }
+ return u.layer.MkdirAll(name, perm)
+}
+
+func (u *CopyOnWriteFs) Name() string {
+ return "CopyOnWriteFs"
+}
+
+func (u *CopyOnWriteFs) MkdirAll(name string, perm os.FileMode) error {
+ dir, err := IsDir(u.base, name)
+ if err != nil {
+ return u.layer.MkdirAll(name, perm)
+ }
+ if dir {
+ // This is in line with how os.MkdirAll behaves.
+ return nil
+ }
+ return u.layer.MkdirAll(name, perm)
+}
+
+func (u *CopyOnWriteFs) Create(name string) (File, error) {
+ return u.OpenFile(name, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0o666)
+}
diff --git a/vendor/github.com/spf13/afero/httpFs.go b/vendor/github.com/spf13/afero/httpFs.go
new file mode 100644
index 00000000..ac0de6d5
--- /dev/null
+++ b/vendor/github.com/spf13/afero/httpFs.go
@@ -0,0 +1,114 @@
+// Copyright © 2014 Steve Francia .
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package afero
+
+import (
+ "errors"
+ "net/http"
+ "os"
+ "path"
+ "path/filepath"
+ "strings"
+ "time"
+)
+
+type httpDir struct {
+ basePath string
+ fs HttpFs
+}
+
+func (d httpDir) Open(name string) (http.File, error) {
+ if filepath.Separator != '/' && strings.ContainsRune(name, filepath.Separator) ||
+ strings.Contains(name, "\x00") {
+ return nil, errors.New("http: invalid character in file path")
+ }
+ dir := string(d.basePath)
+ if dir == "" {
+ dir = "."
+ }
+
+ f, err := d.fs.Open(filepath.Join(dir, filepath.FromSlash(path.Clean("/"+name))))
+ if err != nil {
+ return nil, err
+ }
+ return f, nil
+}
+
+type HttpFs struct {
+ source Fs
+}
+
+func NewHttpFs(source Fs) *HttpFs {
+ return &HttpFs{source: source}
+}
+
+func (h HttpFs) Dir(s string) *httpDir {
+ return &httpDir{basePath: s, fs: h}
+}
+
+func (h HttpFs) Name() string { return "h HttpFs" }
+
+func (h HttpFs) Create(name string) (File, error) {
+ return h.source.Create(name)
+}
+
+func (h HttpFs) Chmod(name string, mode os.FileMode) error {
+ return h.source.Chmod(name, mode)
+}
+
+func (h HttpFs) Chown(name string, uid, gid int) error {
+ return h.source.Chown(name, uid, gid)
+}
+
+func (h HttpFs) Chtimes(name string, atime time.Time, mtime time.Time) error {
+ return h.source.Chtimes(name, atime, mtime)
+}
+
+func (h HttpFs) Mkdir(name string, perm os.FileMode) error {
+ return h.source.Mkdir(name, perm)
+}
+
+func (h HttpFs) MkdirAll(path string, perm os.FileMode) error {
+ return h.source.MkdirAll(path, perm)
+}
+
+func (h HttpFs) Open(name string) (http.File, error) {
+ f, err := h.source.Open(name)
+ if err == nil {
+ if httpfile, ok := f.(http.File); ok {
+ return httpfile, nil
+ }
+ }
+ return nil, err
+}
+
+func (h HttpFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
+ return h.source.OpenFile(name, flag, perm)
+}
+
+func (h HttpFs) Remove(name string) error {
+ return h.source.Remove(name)
+}
+
+func (h HttpFs) RemoveAll(path string) error {
+ return h.source.RemoveAll(path)
+}
+
+func (h HttpFs) Rename(oldname, newname string) error {
+ return h.source.Rename(oldname, newname)
+}
+
+func (h HttpFs) Stat(name string) (os.FileInfo, error) {
+ return h.source.Stat(name)
+}
diff --git a/vendor/github.com/spf13/afero/internal/common/adapters.go b/vendor/github.com/spf13/afero/internal/common/adapters.go
new file mode 100644
index 00000000..60685caa
--- /dev/null
+++ b/vendor/github.com/spf13/afero/internal/common/adapters.go
@@ -0,0 +1,27 @@
+// Copyright © 2022 Steve Francia .
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package common
+
+import "io/fs"
+
+// FileInfoDirEntry provides an adapter from os.FileInfo to fs.DirEntry
+type FileInfoDirEntry struct {
+ fs.FileInfo
+}
+
+var _ fs.DirEntry = FileInfoDirEntry{}
+
+func (d FileInfoDirEntry) Type() fs.FileMode { return d.FileInfo.Mode().Type() }
+
+func (d FileInfoDirEntry) Info() (fs.FileInfo, error) { return d.FileInfo, nil }
diff --git a/vendor/github.com/spf13/afero/iofs.go b/vendor/github.com/spf13/afero/iofs.go
new file mode 100644
index 00000000..938b9316
--- /dev/null
+++ b/vendor/github.com/spf13/afero/iofs.go
@@ -0,0 +1,298 @@
+//go:build go1.16
+// +build go1.16
+
+package afero
+
+import (
+ "io"
+ "io/fs"
+ "os"
+ "path"
+ "sort"
+ "time"
+
+ "github.com/spf13/afero/internal/common"
+)
+
+// IOFS adopts afero.Fs to stdlib io/fs.FS
+type IOFS struct {
+ Fs
+}
+
+func NewIOFS(fs Fs) IOFS {
+ return IOFS{Fs: fs}
+}
+
+var (
+ _ fs.FS = IOFS{}
+ _ fs.GlobFS = IOFS{}
+ _ fs.ReadDirFS = IOFS{}
+ _ fs.ReadFileFS = IOFS{}
+ _ fs.StatFS = IOFS{}
+ _ fs.SubFS = IOFS{}
+)
+
+func (iofs IOFS) Open(name string) (fs.File, error) {
+ const op = "open"
+
+ // by convention for fs.FS implementations we should perform this check
+ if !fs.ValidPath(name) {
+ return nil, iofs.wrapError(op, name, fs.ErrInvalid)
+ }
+
+ file, err := iofs.Fs.Open(name)
+ if err != nil {
+ return nil, iofs.wrapError(op, name, err)
+ }
+
+ // file should implement fs.ReadDirFile
+ if _, ok := file.(fs.ReadDirFile); !ok {
+ file = readDirFile{file}
+ }
+
+ return file, nil
+}
+
+func (iofs IOFS) Glob(pattern string) ([]string, error) {
+ const op = "glob"
+
+ // afero.Glob does not perform this check but it's required for implementations
+ if _, err := path.Match(pattern, ""); err != nil {
+ return nil, iofs.wrapError(op, pattern, err)
+ }
+
+ items, err := Glob(iofs.Fs, pattern)
+ if err != nil {
+ return nil, iofs.wrapError(op, pattern, err)
+ }
+
+ return items, nil
+}
+
+func (iofs IOFS) ReadDir(name string) ([]fs.DirEntry, error) {
+ f, err := iofs.Fs.Open(name)
+ if err != nil {
+ return nil, iofs.wrapError("readdir", name, err)
+ }
+
+ defer f.Close()
+
+ if rdf, ok := f.(fs.ReadDirFile); ok {
+ items, err := rdf.ReadDir(-1)
+ if err != nil {
+ return nil, iofs.wrapError("readdir", name, err)
+ }
+ sort.Slice(items, func(i, j int) bool { return items[i].Name() < items[j].Name() })
+ return items, nil
+ }
+
+ items, err := f.Readdir(-1)
+ if err != nil {
+ return nil, iofs.wrapError("readdir", name, err)
+ }
+ sort.Sort(byName(items))
+
+ ret := make([]fs.DirEntry, len(items))
+ for i := range items {
+ ret[i] = common.FileInfoDirEntry{FileInfo: items[i]}
+ }
+
+ return ret, nil
+}
+
+func (iofs IOFS) ReadFile(name string) ([]byte, error) {
+ const op = "readfile"
+
+ if !fs.ValidPath(name) {
+ return nil, iofs.wrapError(op, name, fs.ErrInvalid)
+ }
+
+ bytes, err := ReadFile(iofs.Fs, name)
+ if err != nil {
+ return nil, iofs.wrapError(op, name, err)
+ }
+
+ return bytes, nil
+}
+
+func (iofs IOFS) Sub(dir string) (fs.FS, error) { return IOFS{NewBasePathFs(iofs.Fs, dir)}, nil }
+
+func (IOFS) wrapError(op, path string, err error) error {
+ if _, ok := err.(*fs.PathError); ok {
+ return err // don't need to wrap again
+ }
+
+ return &fs.PathError{
+ Op: op,
+ Path: path,
+ Err: err,
+ }
+}
+
+// readDirFile provides adapter from afero.File to fs.ReadDirFile needed for correct Open
+type readDirFile struct {
+ File
+}
+
+var _ fs.ReadDirFile = readDirFile{}
+
+func (r readDirFile) ReadDir(n int) ([]fs.DirEntry, error) {
+ items, err := r.File.Readdir(n)
+ if err != nil {
+ return nil, err
+ }
+
+ ret := make([]fs.DirEntry, len(items))
+ for i := range items {
+ ret[i] = common.FileInfoDirEntry{FileInfo: items[i]}
+ }
+
+ return ret, nil
+}
+
+// FromIOFS adopts io/fs.FS to use it as afero.Fs
+// Note that io/fs.FS is read-only so all mutating methods will return fs.PathError with fs.ErrPermission
+// To store modifications you may use afero.CopyOnWriteFs
+type FromIOFS struct {
+ fs.FS
+}
+
+var _ Fs = FromIOFS{}
+
+func (f FromIOFS) Create(name string) (File, error) { return nil, notImplemented("create", name) }
+
+func (f FromIOFS) Mkdir(name string, perm os.FileMode) error { return notImplemented("mkdir", name) }
+
+func (f FromIOFS) MkdirAll(path string, perm os.FileMode) error {
+ return notImplemented("mkdirall", path)
+}
+
+func (f FromIOFS) Open(name string) (File, error) {
+ file, err := f.FS.Open(name)
+ if err != nil {
+ return nil, err
+ }
+
+ return fromIOFSFile{File: file, name: name}, nil
+}
+
+func (f FromIOFS) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
+ return f.Open(name)
+}
+
+func (f FromIOFS) Remove(name string) error {
+ return notImplemented("remove", name)
+}
+
+func (f FromIOFS) RemoveAll(path string) error {
+ return notImplemented("removeall", path)
+}
+
+func (f FromIOFS) Rename(oldname, newname string) error {
+ return notImplemented("rename", oldname)
+}
+
+func (f FromIOFS) Stat(name string) (os.FileInfo, error) { return fs.Stat(f.FS, name) }
+
+func (f FromIOFS) Name() string { return "fromiofs" }
+
+func (f FromIOFS) Chmod(name string, mode os.FileMode) error {
+ return notImplemented("chmod", name)
+}
+
+func (f FromIOFS) Chown(name string, uid, gid int) error {
+ return notImplemented("chown", name)
+}
+
+func (f FromIOFS) Chtimes(name string, atime time.Time, mtime time.Time) error {
+ return notImplemented("chtimes", name)
+}
+
+type fromIOFSFile struct {
+ fs.File
+ name string
+}
+
+func (f fromIOFSFile) ReadAt(p []byte, off int64) (n int, err error) {
+ readerAt, ok := f.File.(io.ReaderAt)
+ if !ok {
+ return -1, notImplemented("readat", f.name)
+ }
+
+ return readerAt.ReadAt(p, off)
+}
+
+func (f fromIOFSFile) Seek(offset int64, whence int) (int64, error) {
+ seeker, ok := f.File.(io.Seeker)
+ if !ok {
+ return -1, notImplemented("seek", f.name)
+ }
+
+ return seeker.Seek(offset, whence)
+}
+
+func (f fromIOFSFile) Write(p []byte) (n int, err error) {
+ return -1, notImplemented("write", f.name)
+}
+
+func (f fromIOFSFile) WriteAt(p []byte, off int64) (n int, err error) {
+ return -1, notImplemented("writeat", f.name)
+}
+
+func (f fromIOFSFile) Name() string { return f.name }
+
+func (f fromIOFSFile) Readdir(count int) ([]os.FileInfo, error) {
+ rdfile, ok := f.File.(fs.ReadDirFile)
+ if !ok {
+ return nil, notImplemented("readdir", f.name)
+ }
+
+ entries, err := rdfile.ReadDir(count)
+ if err != nil {
+ return nil, err
+ }
+
+ ret := make([]os.FileInfo, len(entries))
+ for i := range entries {
+ ret[i], err = entries[i].Info()
+
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ return ret, nil
+}
+
+func (f fromIOFSFile) Readdirnames(n int) ([]string, error) {
+ rdfile, ok := f.File.(fs.ReadDirFile)
+ if !ok {
+ return nil, notImplemented("readdir", f.name)
+ }
+
+ entries, err := rdfile.ReadDir(n)
+ if err != nil {
+ return nil, err
+ }
+
+ ret := make([]string, len(entries))
+ for i := range entries {
+ ret[i] = entries[i].Name()
+ }
+
+ return ret, nil
+}
+
+func (f fromIOFSFile) Sync() error { return nil }
+
+func (f fromIOFSFile) Truncate(size int64) error {
+ return notImplemented("truncate", f.name)
+}
+
+func (f fromIOFSFile) WriteString(s string) (ret int, err error) {
+ return -1, notImplemented("writestring", f.name)
+}
+
+func notImplemented(op, path string) error {
+ return &fs.PathError{Op: op, Path: path, Err: fs.ErrPermission}
+}
diff --git a/vendor/github.com/spf13/afero/ioutil.go b/vendor/github.com/spf13/afero/ioutil.go
new file mode 100644
index 00000000..fa6abe1e
--- /dev/null
+++ b/vendor/github.com/spf13/afero/ioutil.go
@@ -0,0 +1,243 @@
+// Copyright ©2015 The Go Authors
+// Copyright ©2015 Steve Francia
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package afero
+
+import (
+ "bytes"
+ "io"
+ "os"
+ "path/filepath"
+ "sort"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+)
+
+// byName implements sort.Interface.
+type byName []os.FileInfo
+
+func (f byName) Len() int { return len(f) }
+func (f byName) Less(i, j int) bool { return f[i].Name() < f[j].Name() }
+func (f byName) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
+
+// ReadDir reads the directory named by dirname and returns
+// a list of sorted directory entries.
+func (a Afero) ReadDir(dirname string) ([]os.FileInfo, error) {
+ return ReadDir(a.Fs, dirname)
+}
+
+func ReadDir(fs Fs, dirname string) ([]os.FileInfo, error) {
+ f, err := fs.Open(dirname)
+ if err != nil {
+ return nil, err
+ }
+ list, err := f.Readdir(-1)
+ f.Close()
+ if err != nil {
+ return nil, err
+ }
+ sort.Sort(byName(list))
+ return list, nil
+}
+
+// ReadFile reads the file named by filename and returns the contents.
+// A successful call returns err == nil, not err == EOF. Because ReadFile
+// reads the whole file, it does not treat an EOF from Read as an error
+// to be reported.
+func (a Afero) ReadFile(filename string) ([]byte, error) {
+ return ReadFile(a.Fs, filename)
+}
+
+func ReadFile(fs Fs, filename string) ([]byte, error) {
+ f, err := fs.Open(filename)
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+ // It's a good but not certain bet that FileInfo will tell us exactly how much to
+ // read, so let's try it but be prepared for the answer to be wrong.
+ var n int64
+
+ if fi, err := f.Stat(); err == nil {
+ // Don't preallocate a huge buffer, just in case.
+ if size := fi.Size(); size < 1e9 {
+ n = size
+ }
+ }
+ // As initial capacity for readAll, use n + a little extra in case Size is zero,
+ // and to avoid another allocation after Read has filled the buffer. The readAll
+ // call will read into its allocated internal buffer cheaply. If the size was
+ // wrong, we'll either waste some space off the end or reallocate as needed, but
+ // in the overwhelmingly common case we'll get it just right.
+ return readAll(f, n+bytes.MinRead)
+}
+
+// readAll reads from r until an error or EOF and returns the data it read
+// from the internal buffer allocated with a specified capacity.
+func readAll(r io.Reader, capacity int64) (b []byte, err error) {
+ buf := bytes.NewBuffer(make([]byte, 0, capacity))
+ // If the buffer overflows, we will get bytes.ErrTooLarge.
+ // Return that as an error. Any other panic remains.
+ defer func() {
+ e := recover()
+ if e == nil {
+ return
+ }
+ if panicErr, ok := e.(error); ok && panicErr == bytes.ErrTooLarge {
+ err = panicErr
+ } else {
+ panic(e)
+ }
+ }()
+ _, err = buf.ReadFrom(r)
+ return buf.Bytes(), err
+}
+
+// ReadAll reads from r until an error or EOF and returns the data it read.
+// A successful call returns err == nil, not err == EOF. Because ReadAll is
+// defined to read from src until EOF, it does not treat an EOF from Read
+// as an error to be reported.
+func ReadAll(r io.Reader) ([]byte, error) {
+ return readAll(r, bytes.MinRead)
+}
+
+// WriteFile writes data to a file named by filename.
+// If the file does not exist, WriteFile creates it with permissions perm;
+// otherwise WriteFile truncates it before writing.
+func (a Afero) WriteFile(filename string, data []byte, perm os.FileMode) error {
+ return WriteFile(a.Fs, filename, data, perm)
+}
+
+func WriteFile(fs Fs, filename string, data []byte, perm os.FileMode) error {
+ f, err := fs.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
+ if err != nil {
+ return err
+ }
+ n, err := f.Write(data)
+ if err == nil && n < len(data) {
+ err = io.ErrShortWrite
+ }
+ if err1 := f.Close(); err == nil {
+ err = err1
+ }
+ return err
+}
+
+// Random number state.
+// We generate random temporary file names so that there's a good
+// chance the file doesn't exist yet - keeps the number of tries in
+// TempFile to a minimum.
+var (
+ randNum uint32
+ randmu sync.Mutex
+)
+
+func reseed() uint32 {
+ return uint32(time.Now().UnixNano() + int64(os.Getpid()))
+}
+
+func nextRandom() string {
+ randmu.Lock()
+ r := randNum
+ if r == 0 {
+ r = reseed()
+ }
+ r = r*1664525 + 1013904223 // constants from Numerical Recipes
+ randNum = r
+ randmu.Unlock()
+ return strconv.Itoa(int(1e9 + r%1e9))[1:]
+}
+
+// TempFile creates a new temporary file in the directory dir,
+// opens the file for reading and writing, and returns the resulting *os.File.
+// The filename is generated by taking pattern and adding a random
+// string to the end. If pattern includes a "*", the random string
+// replaces the last "*".
+// If dir is the empty string, TempFile uses the default directory
+// for temporary files (see os.TempDir).
+// Multiple programs calling TempFile simultaneously
+// will not choose the same file. The caller can use f.Name()
+// to find the pathname of the file. It is the caller's responsibility
+// to remove the file when no longer needed.
+func (a Afero) TempFile(dir, pattern string) (f File, err error) {
+ return TempFile(a.Fs, dir, pattern)
+}
+
+func TempFile(fs Fs, dir, pattern string) (f File, err error) {
+ if dir == "" {
+ dir = os.TempDir()
+ }
+
+ var prefix, suffix string
+ if pos := strings.LastIndex(pattern, "*"); pos != -1 {
+ prefix, suffix = pattern[:pos], pattern[pos+1:]
+ } else {
+ prefix = pattern
+ }
+
+ nconflict := 0
+ for i := 0; i < 10000; i++ {
+ name := filepath.Join(dir, prefix+nextRandom()+suffix)
+ f, err = fs.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0o600)
+ if os.IsExist(err) {
+ if nconflict++; nconflict > 10 {
+ randmu.Lock()
+ randNum = reseed()
+ randmu.Unlock()
+ }
+ continue
+ }
+ break
+ }
+ return
+}
+
+// TempDir creates a new temporary directory in the directory dir
+// with a name beginning with prefix and returns the path of the
+// new directory. If dir is the empty string, TempDir uses the
+// default directory for temporary files (see os.TempDir).
+// Multiple programs calling TempDir simultaneously
+// will not choose the same directory. It is the caller's responsibility
+// to remove the directory when no longer needed.
+func (a Afero) TempDir(dir, prefix string) (name string, err error) {
+ return TempDir(a.Fs, dir, prefix)
+}
+
+func TempDir(fs Fs, dir, prefix string) (name string, err error) {
+ if dir == "" {
+ dir = os.TempDir()
+ }
+
+ nconflict := 0
+ for i := 0; i < 10000; i++ {
+ try := filepath.Join(dir, prefix+nextRandom())
+ err = fs.Mkdir(try, 0o700)
+ if os.IsExist(err) {
+ if nconflict++; nconflict > 10 {
+ randmu.Lock()
+ randNum = reseed()
+ randmu.Unlock()
+ }
+ continue
+ }
+ if err == nil {
+ name = try
+ }
+ break
+ }
+ return
+}
diff --git a/vendor/github.com/spf13/afero/lstater.go b/vendor/github.com/spf13/afero/lstater.go
new file mode 100644
index 00000000..89c1bfc0
--- /dev/null
+++ b/vendor/github.com/spf13/afero/lstater.go
@@ -0,0 +1,27 @@
+// Copyright © 2018 Steve Francia .
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package afero
+
+import (
+ "os"
+)
+
+// Lstater is an optional interface in Afero. It is only implemented by the
+// filesystems saying so.
+// It will call Lstat if the filesystem iself is, or it delegates to, the os filesystem.
+// Else it will call Stat.
+// In addtion to the FileInfo, it will return a boolean telling whether Lstat was called or not.
+type Lstater interface {
+ LstatIfPossible(name string) (os.FileInfo, bool, error)
+}
diff --git a/vendor/github.com/spf13/afero/match.go b/vendor/github.com/spf13/afero/match.go
new file mode 100644
index 00000000..7db4b7de
--- /dev/null
+++ b/vendor/github.com/spf13/afero/match.go
@@ -0,0 +1,110 @@
+// Copyright © 2014 Steve Francia .
+// Copyright 2009 The Go Authors. All rights reserved.
+
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package afero
+
+import (
+ "path/filepath"
+ "sort"
+ "strings"
+)
+
+// Glob returns the names of all files matching pattern or nil
+// if there is no matching file. The syntax of patterns is the same
+// as in Match. The pattern may describe hierarchical names such as
+// /usr/*/bin/ed (assuming the Separator is '/').
+//
+// Glob ignores file system errors such as I/O errors reading directories.
+// The only possible returned error is ErrBadPattern, when pattern
+// is malformed.
+//
+// This was adapted from (http://golang.org/pkg/path/filepath) and uses several
+// built-ins from that package.
+func Glob(fs Fs, pattern string) (matches []string, err error) {
+ if !hasMeta(pattern) {
+ // Lstat not supported by a ll filesystems.
+ if _, err = lstatIfPossible(fs, pattern); err != nil {
+ return nil, nil
+ }
+ return []string{pattern}, nil
+ }
+
+ dir, file := filepath.Split(pattern)
+ switch dir {
+ case "":
+ dir = "."
+ case string(filepath.Separator):
+ // nothing
+ default:
+ dir = dir[0 : len(dir)-1] // chop off trailing separator
+ }
+
+ if !hasMeta(dir) {
+ return glob(fs, dir, file, nil)
+ }
+
+ var m []string
+ m, err = Glob(fs, dir)
+ if err != nil {
+ return
+ }
+ for _, d := range m {
+ matches, err = glob(fs, d, file, matches)
+ if err != nil {
+ return
+ }
+ }
+ return
+}
+
+// glob searches for files matching pattern in the directory dir
+// and appends them to matches. If the directory cannot be
+// opened, it returns the existing matches. New matches are
+// added in lexicographical order.
+func glob(fs Fs, dir, pattern string, matches []string) (m []string, e error) {
+ m = matches
+ fi, err := fs.Stat(dir)
+ if err != nil {
+ return
+ }
+ if !fi.IsDir() {
+ return
+ }
+ d, err := fs.Open(dir)
+ if err != nil {
+ return
+ }
+ defer d.Close()
+
+ names, _ := d.Readdirnames(-1)
+ sort.Strings(names)
+
+ for _, n := range names {
+ matched, err := filepath.Match(pattern, n)
+ if err != nil {
+ return m, err
+ }
+ if matched {
+ m = append(m, filepath.Join(dir, n))
+ }
+ }
+ return
+}
+
+// hasMeta reports whether path contains any of the magic characters
+// recognized by Match.
+func hasMeta(path string) bool {
+ // TODO(niemeyer): Should other magic characters be added here?
+ return strings.ContainsAny(path, "*?[")
+}
diff --git a/vendor/github.com/spf13/afero/mem/dir.go b/vendor/github.com/spf13/afero/mem/dir.go
new file mode 100644
index 00000000..e104013f
--- /dev/null
+++ b/vendor/github.com/spf13/afero/mem/dir.go
@@ -0,0 +1,37 @@
+// Copyright © 2014 Steve Francia .
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package mem
+
+type Dir interface {
+ Len() int
+ Names() []string
+ Files() []*FileData
+ Add(*FileData)
+ Remove(*FileData)
+}
+
+func RemoveFromMemDir(dir *FileData, f *FileData) {
+ dir.memDir.Remove(f)
+}
+
+func AddToMemDir(dir *FileData, f *FileData) {
+ dir.memDir.Add(f)
+}
+
+func InitializeDir(d *FileData) {
+ if d.memDir == nil {
+ d.dir = true
+ d.memDir = &DirMap{}
+ }
+}
diff --git a/vendor/github.com/spf13/afero/mem/dirmap.go b/vendor/github.com/spf13/afero/mem/dirmap.go
new file mode 100644
index 00000000..03a57ee5
--- /dev/null
+++ b/vendor/github.com/spf13/afero/mem/dirmap.go
@@ -0,0 +1,43 @@
+// Copyright © 2015 Steve Francia .
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package mem
+
+import "sort"
+
+type DirMap map[string]*FileData
+
+func (m DirMap) Len() int { return len(m) }
+func (m DirMap) Add(f *FileData) { m[f.name] = f }
+func (m DirMap) Remove(f *FileData) { delete(m, f.name) }
+func (m DirMap) Files() (files []*FileData) {
+ for _, f := range m {
+ files = append(files, f)
+ }
+ sort.Sort(filesSorter(files))
+ return files
+}
+
+// implement sort.Interface for []*FileData
+type filesSorter []*FileData
+
+func (s filesSorter) Len() int { return len(s) }
+func (s filesSorter) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+func (s filesSorter) Less(i, j int) bool { return s[i].name < s[j].name }
+
+func (m DirMap) Names() (names []string) {
+ for x := range m {
+ names = append(names, x)
+ }
+ return names
+}
diff --git a/vendor/github.com/spf13/afero/mem/file.go b/vendor/github.com/spf13/afero/mem/file.go
new file mode 100644
index 00000000..62fe4498
--- /dev/null
+++ b/vendor/github.com/spf13/afero/mem/file.go
@@ -0,0 +1,359 @@
+// Copyright © 2015 Steve Francia .
+// Copyright 2013 tsuru authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package mem
+
+import (
+ "bytes"
+ "errors"
+ "io"
+ "io/fs"
+ "os"
+ "path/filepath"
+ "sync"
+ "sync/atomic"
+ "time"
+
+ "github.com/spf13/afero/internal/common"
+)
+
+const FilePathSeparator = string(filepath.Separator)
+
+var _ fs.ReadDirFile = &File{}
+
+type File struct {
+ // atomic requires 64-bit alignment for struct field access
+ at int64
+ readDirCount int64
+ closed bool
+ readOnly bool
+ fileData *FileData
+}
+
+func NewFileHandle(data *FileData) *File {
+ return &File{fileData: data}
+}
+
+func NewReadOnlyFileHandle(data *FileData) *File {
+ return &File{fileData: data, readOnly: true}
+}
+
+func (f File) Data() *FileData {
+ return f.fileData
+}
+
+type FileData struct {
+ sync.Mutex
+ name string
+ data []byte
+ memDir Dir
+ dir bool
+ mode os.FileMode
+ modtime time.Time
+ uid int
+ gid int
+}
+
+func (d *FileData) Name() string {
+ d.Lock()
+ defer d.Unlock()
+ return d.name
+}
+
+func CreateFile(name string) *FileData {
+ return &FileData{name: name, mode: os.ModeTemporary, modtime: time.Now()}
+}
+
+func CreateDir(name string) *FileData {
+ return &FileData{name: name, memDir: &DirMap{}, dir: true, modtime: time.Now()}
+}
+
+func ChangeFileName(f *FileData, newname string) {
+ f.Lock()
+ f.name = newname
+ f.Unlock()
+}
+
+func SetMode(f *FileData, mode os.FileMode) {
+ f.Lock()
+ f.mode = mode
+ f.Unlock()
+}
+
+func SetModTime(f *FileData, mtime time.Time) {
+ f.Lock()
+ setModTime(f, mtime)
+ f.Unlock()
+}
+
+func setModTime(f *FileData, mtime time.Time) {
+ f.modtime = mtime
+}
+
+func SetUID(f *FileData, uid int) {
+ f.Lock()
+ f.uid = uid
+ f.Unlock()
+}
+
+func SetGID(f *FileData, gid int) {
+ f.Lock()
+ f.gid = gid
+ f.Unlock()
+}
+
+func GetFileInfo(f *FileData) *FileInfo {
+ return &FileInfo{f}
+}
+
+func (f *File) Open() error {
+ atomic.StoreInt64(&f.at, 0)
+ atomic.StoreInt64(&f.readDirCount, 0)
+ f.fileData.Lock()
+ f.closed = false
+ f.fileData.Unlock()
+ return nil
+}
+
+func (f *File) Close() error {
+ f.fileData.Lock()
+ f.closed = true
+ if !f.readOnly {
+ setModTime(f.fileData, time.Now())
+ }
+ f.fileData.Unlock()
+ return nil
+}
+
+func (f *File) Name() string {
+ return f.fileData.Name()
+}
+
+func (f *File) Stat() (os.FileInfo, error) {
+ return &FileInfo{f.fileData}, nil
+}
+
+func (f *File) Sync() error {
+ return nil
+}
+
+func (f *File) Readdir(count int) (res []os.FileInfo, err error) {
+ if !f.fileData.dir {
+ return nil, &os.PathError{Op: "readdir", Path: f.fileData.name, Err: errors.New("not a dir")}
+ }
+ var outLength int64
+
+ f.fileData.Lock()
+ files := f.fileData.memDir.Files()[f.readDirCount:]
+ if count > 0 {
+ if len(files) < count {
+ outLength = int64(len(files))
+ } else {
+ outLength = int64(count)
+ }
+ if len(files) == 0 {
+ err = io.EOF
+ }
+ } else {
+ outLength = int64(len(files))
+ }
+ f.readDirCount += outLength
+ f.fileData.Unlock()
+
+ res = make([]os.FileInfo, outLength)
+ for i := range res {
+ res[i] = &FileInfo{files[i]}
+ }
+
+ return res, err
+}
+
+func (f *File) Readdirnames(n int) (names []string, err error) {
+ fi, err := f.Readdir(n)
+ names = make([]string, len(fi))
+ for i, f := range fi {
+ _, names[i] = filepath.Split(f.Name())
+ }
+ return names, err
+}
+
+// Implements fs.ReadDirFile
+func (f *File) ReadDir(n int) ([]fs.DirEntry, error) {
+ fi, err := f.Readdir(n)
+ if err != nil {
+ return nil, err
+ }
+ di := make([]fs.DirEntry, len(fi))
+ for i, f := range fi {
+ di[i] = common.FileInfoDirEntry{FileInfo: f}
+ }
+ return di, nil
+}
+
+func (f *File) Read(b []byte) (n int, err error) {
+ f.fileData.Lock()
+ defer f.fileData.Unlock()
+ if f.closed {
+ return 0, ErrFileClosed
+ }
+ if len(b) > 0 && int(f.at) == len(f.fileData.data) {
+ return 0, io.EOF
+ }
+ if int(f.at) > len(f.fileData.data) {
+ return 0, io.ErrUnexpectedEOF
+ }
+ if len(f.fileData.data)-int(f.at) >= len(b) {
+ n = len(b)
+ } else {
+ n = len(f.fileData.data) - int(f.at)
+ }
+ copy(b, f.fileData.data[f.at:f.at+int64(n)])
+ atomic.AddInt64(&f.at, int64(n))
+ return
+}
+
+func (f *File) ReadAt(b []byte, off int64) (n int, err error) {
+ prev := atomic.LoadInt64(&f.at)
+ atomic.StoreInt64(&f.at, off)
+ n, err = f.Read(b)
+ atomic.StoreInt64(&f.at, prev)
+ return
+}
+
+func (f *File) Truncate(size int64) error {
+ if f.closed {
+ return ErrFileClosed
+ }
+ if f.readOnly {
+ return &os.PathError{Op: "truncate", Path: f.fileData.name, Err: errors.New("file handle is read only")}
+ }
+ if size < 0 {
+ return ErrOutOfRange
+ }
+ f.fileData.Lock()
+ defer f.fileData.Unlock()
+ if size > int64(len(f.fileData.data)) {
+ diff := size - int64(len(f.fileData.data))
+ f.fileData.data = append(f.fileData.data, bytes.Repeat([]byte{0o0}, int(diff))...)
+ } else {
+ f.fileData.data = f.fileData.data[0:size]
+ }
+ setModTime(f.fileData, time.Now())
+ return nil
+}
+
+func (f *File) Seek(offset int64, whence int) (int64, error) {
+ if f.closed {
+ return 0, ErrFileClosed
+ }
+ switch whence {
+ case io.SeekStart:
+ atomic.StoreInt64(&f.at, offset)
+ case io.SeekCurrent:
+ atomic.AddInt64(&f.at, offset)
+ case io.SeekEnd:
+ atomic.StoreInt64(&f.at, int64(len(f.fileData.data))+offset)
+ }
+ return f.at, nil
+}
+
+func (f *File) Write(b []byte) (n int, err error) {
+ if f.closed {
+ return 0, ErrFileClosed
+ }
+ if f.readOnly {
+ return 0, &os.PathError{Op: "write", Path: f.fileData.name, Err: errors.New("file handle is read only")}
+ }
+ n = len(b)
+ cur := atomic.LoadInt64(&f.at)
+ f.fileData.Lock()
+ defer f.fileData.Unlock()
+ diff := cur - int64(len(f.fileData.data))
+ var tail []byte
+ if n+int(cur) < len(f.fileData.data) {
+ tail = f.fileData.data[n+int(cur):]
+ }
+ if diff > 0 {
+ f.fileData.data = append(f.fileData.data, append(bytes.Repeat([]byte{0o0}, int(diff)), b...)...)
+ f.fileData.data = append(f.fileData.data, tail...)
+ } else {
+ f.fileData.data = append(f.fileData.data[:cur], b...)
+ f.fileData.data = append(f.fileData.data, tail...)
+ }
+ setModTime(f.fileData, time.Now())
+
+ atomic.AddInt64(&f.at, int64(n))
+ return
+}
+
+func (f *File) WriteAt(b []byte, off int64) (n int, err error) {
+ atomic.StoreInt64(&f.at, off)
+ return f.Write(b)
+}
+
+func (f *File) WriteString(s string) (ret int, err error) {
+ return f.Write([]byte(s))
+}
+
+func (f *File) Info() *FileInfo {
+ return &FileInfo{f.fileData}
+}
+
+type FileInfo struct {
+ *FileData
+}
+
+// Implements os.FileInfo
+func (s *FileInfo) Name() string {
+ s.Lock()
+ _, name := filepath.Split(s.name)
+ s.Unlock()
+ return name
+}
+
+func (s *FileInfo) Mode() os.FileMode {
+ s.Lock()
+ defer s.Unlock()
+ return s.mode
+}
+
+func (s *FileInfo) ModTime() time.Time {
+ s.Lock()
+ defer s.Unlock()
+ return s.modtime
+}
+
+func (s *FileInfo) IsDir() bool {
+ s.Lock()
+ defer s.Unlock()
+ return s.dir
+}
+func (s *FileInfo) Sys() interface{} { return nil }
+func (s *FileInfo) Size() int64 {
+ if s.IsDir() {
+ return int64(42)
+ }
+ s.Lock()
+ defer s.Unlock()
+ return int64(len(s.data))
+}
+
+var (
+ ErrFileClosed = errors.New("File is closed")
+ ErrOutOfRange = errors.New("out of range")
+ ErrTooLarge = errors.New("too large")
+ ErrFileNotFound = os.ErrNotExist
+ ErrFileExists = os.ErrExist
+ ErrDestinationExists = os.ErrExist
+)
diff --git a/vendor/github.com/spf13/afero/memmap.go b/vendor/github.com/spf13/afero/memmap.go
new file mode 100644
index 00000000..d6c744e8
--- /dev/null
+++ b/vendor/github.com/spf13/afero/memmap.go
@@ -0,0 +1,465 @@
+// Copyright © 2014 Steve Francia .
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package afero
+
+import (
+ "fmt"
+ "io"
+
+ "log"
+ "os"
+ "path/filepath"
+
+ "sort"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/spf13/afero/mem"
+)
+
+const chmodBits = os.ModePerm | os.ModeSetuid | os.ModeSetgid | os.ModeSticky // Only a subset of bits are allowed to be changed. Documented under os.Chmod()
+
+type MemMapFs struct {
+ mu sync.RWMutex
+ data map[string]*mem.FileData
+ init sync.Once
+}
+
+func NewMemMapFs() Fs {
+ return &MemMapFs{}
+}
+
+func (m *MemMapFs) getData() map[string]*mem.FileData {
+ m.init.Do(func() {
+ m.data = make(map[string]*mem.FileData)
+ // Root should always exist, right?
+ // TODO: what about windows?
+ root := mem.CreateDir(FilePathSeparator)
+ mem.SetMode(root, os.ModeDir|0o755)
+ m.data[FilePathSeparator] = root
+ })
+ return m.data
+}
+
+func (*MemMapFs) Name() string { return "MemMapFS" }
+
+func (m *MemMapFs) Create(name string) (File, error) {
+ name = normalizePath(name)
+ m.mu.Lock()
+ file := mem.CreateFile(name)
+ m.getData()[name] = file
+ m.registerWithParent(file, 0)
+ m.mu.Unlock()
+ return mem.NewFileHandle(file), nil
+}
+
+func (m *MemMapFs) unRegisterWithParent(fileName string) error {
+ f, err := m.lockfreeOpen(fileName)
+ if err != nil {
+ return err
+ }
+ parent := m.findParent(f)
+ if parent == nil {
+ log.Panic("parent of ", f.Name(), " is nil")
+ }
+
+ parent.Lock()
+ mem.RemoveFromMemDir(parent, f)
+ parent.Unlock()
+ return nil
+}
+
+func (m *MemMapFs) findParent(f *mem.FileData) *mem.FileData {
+ pdir, _ := filepath.Split(f.Name())
+ pdir = filepath.Clean(pdir)
+ pfile, err := m.lockfreeOpen(pdir)
+ if err != nil {
+ return nil
+ }
+ return pfile
+}
+
+func (m *MemMapFs) findDescendants(name string) []*mem.FileData {
+ fData := m.getData()
+ descendants := make([]*mem.FileData, 0, len(fData))
+ for p, dFile := range fData {
+ if strings.HasPrefix(p, name+FilePathSeparator) {
+ descendants = append(descendants, dFile)
+ }
+ }
+
+ sort.Slice(descendants, func(i, j int) bool {
+ cur := len(strings.Split(descendants[i].Name(), FilePathSeparator))
+ next := len(strings.Split(descendants[j].Name(), FilePathSeparator))
+ return cur < next
+ })
+
+ return descendants
+}
+
+func (m *MemMapFs) registerWithParent(f *mem.FileData, perm os.FileMode) {
+ if f == nil {
+ return
+ }
+ parent := m.findParent(f)
+ if parent == nil {
+ pdir := filepath.Dir(filepath.Clean(f.Name()))
+ err := m.lockfreeMkdir(pdir, perm)
+ if err != nil {
+ // log.Println("Mkdir error:", err)
+ return
+ }
+ parent, err = m.lockfreeOpen(pdir)
+ if err != nil {
+ // log.Println("Open after Mkdir error:", err)
+ return
+ }
+ }
+
+ parent.Lock()
+ mem.InitializeDir(parent)
+ mem.AddToMemDir(parent, f)
+ parent.Unlock()
+}
+
+func (m *MemMapFs) lockfreeMkdir(name string, perm os.FileMode) error {
+ name = normalizePath(name)
+ x, ok := m.getData()[name]
+ if ok {
+ // Only return ErrFileExists if it's a file, not a directory.
+ i := mem.FileInfo{FileData: x}
+ if !i.IsDir() {
+ return ErrFileExists
+ }
+ } else {
+ item := mem.CreateDir(name)
+ mem.SetMode(item, os.ModeDir|perm)
+ m.getData()[name] = item
+ m.registerWithParent(item, perm)
+ }
+ return nil
+}
+
+func (m *MemMapFs) Mkdir(name string, perm os.FileMode) error {
+ perm &= chmodBits
+ name = normalizePath(name)
+
+ m.mu.RLock()
+ _, ok := m.getData()[name]
+ m.mu.RUnlock()
+ if ok {
+ return &os.PathError{Op: "mkdir", Path: name, Err: ErrFileExists}
+ }
+
+ m.mu.Lock()
+ // Dobule check that it doesn't exist.
+ if _, ok := m.getData()[name]; ok {
+ m.mu.Unlock()
+ return &os.PathError{Op: "mkdir", Path: name, Err: ErrFileExists}
+ }
+ item := mem.CreateDir(name)
+ mem.SetMode(item, os.ModeDir|perm)
+ m.getData()[name] = item
+ m.registerWithParent(item, perm)
+ m.mu.Unlock()
+
+ return m.setFileMode(name, perm|os.ModeDir)
+}
+
+func (m *MemMapFs) MkdirAll(path string, perm os.FileMode) error {
+ err := m.Mkdir(path, perm)
+ if err != nil {
+ if err.(*os.PathError).Err == ErrFileExists {
+ return nil
+ }
+ return err
+ }
+ return nil
+}
+
+// Handle some relative paths
+func normalizePath(path string) string {
+ path = filepath.Clean(path)
+
+ switch path {
+ case ".":
+ return FilePathSeparator
+ case "..":
+ return FilePathSeparator
+ default:
+ return path
+ }
+}
+
+func (m *MemMapFs) Open(name string) (File, error) {
+ f, err := m.open(name)
+ if f != nil {
+ return mem.NewReadOnlyFileHandle(f), err
+ }
+ return nil, err
+}
+
+func (m *MemMapFs) openWrite(name string) (File, error) {
+ f, err := m.open(name)
+ if f != nil {
+ return mem.NewFileHandle(f), err
+ }
+ return nil, err
+}
+
+func (m *MemMapFs) open(name string) (*mem.FileData, error) {
+ name = normalizePath(name)
+
+ m.mu.RLock()
+ f, ok := m.getData()[name]
+ m.mu.RUnlock()
+ if !ok {
+ return nil, &os.PathError{Op: "open", Path: name, Err: ErrFileNotFound}
+ }
+ return f, nil
+}
+
+func (m *MemMapFs) lockfreeOpen(name string) (*mem.FileData, error) {
+ name = normalizePath(name)
+ f, ok := m.getData()[name]
+ if ok {
+ return f, nil
+ } else {
+ return nil, ErrFileNotFound
+ }
+}
+
+func (m *MemMapFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
+ perm &= chmodBits
+ chmod := false
+ file, err := m.openWrite(name)
+ if err == nil && (flag&os.O_EXCL > 0) {
+ return nil, &os.PathError{Op: "open", Path: name, Err: ErrFileExists}
+ }
+ if os.IsNotExist(err) && (flag&os.O_CREATE > 0) {
+ file, err = m.Create(name)
+ chmod = true
+ }
+ if err != nil {
+ return nil, err
+ }
+ if flag == os.O_RDONLY {
+ file = mem.NewReadOnlyFileHandle(file.(*mem.File).Data())
+ }
+ if flag&os.O_APPEND > 0 {
+ _, err = file.Seek(0, io.SeekEnd)
+ if err != nil {
+ file.Close()
+ return nil, err
+ }
+ }
+ if flag&os.O_TRUNC > 0 && flag&(os.O_RDWR|os.O_WRONLY) > 0 {
+ err = file.Truncate(0)
+ if err != nil {
+ file.Close()
+ return nil, err
+ }
+ }
+ if chmod {
+ return file, m.setFileMode(name, perm)
+ }
+ return file, nil
+}
+
+func (m *MemMapFs) Remove(name string) error {
+ name = normalizePath(name)
+
+ m.mu.Lock()
+ defer m.mu.Unlock()
+
+ if _, ok := m.getData()[name]; ok {
+ err := m.unRegisterWithParent(name)
+ if err != nil {
+ return &os.PathError{Op: "remove", Path: name, Err: err}
+ }
+ delete(m.getData(), name)
+ } else {
+ return &os.PathError{Op: "remove", Path: name, Err: os.ErrNotExist}
+ }
+ return nil
+}
+
+func (m *MemMapFs) RemoveAll(path string) error {
+ path = normalizePath(path)
+ m.mu.Lock()
+ m.unRegisterWithParent(path)
+ m.mu.Unlock()
+
+ m.mu.RLock()
+ defer m.mu.RUnlock()
+
+ for p := range m.getData() {
+ if p == path || strings.HasPrefix(p, path+FilePathSeparator) {
+ m.mu.RUnlock()
+ m.mu.Lock()
+ delete(m.getData(), p)
+ m.mu.Unlock()
+ m.mu.RLock()
+ }
+ }
+ return nil
+}
+
+func (m *MemMapFs) Rename(oldname, newname string) error {
+ oldname = normalizePath(oldname)
+ newname = normalizePath(newname)
+
+ if oldname == newname {
+ return nil
+ }
+
+ m.mu.RLock()
+ defer m.mu.RUnlock()
+ if _, ok := m.getData()[oldname]; ok {
+ m.mu.RUnlock()
+ m.mu.Lock()
+ err := m.unRegisterWithParent(oldname)
+ if err != nil {
+ return err
+ }
+
+ fileData := m.getData()[oldname]
+ mem.ChangeFileName(fileData, newname)
+ m.getData()[newname] = fileData
+
+ err = m.renameDescendants(oldname, newname)
+ if err != nil {
+ return err
+ }
+
+ delete(m.getData(), oldname)
+
+ m.registerWithParent(fileData, 0)
+ m.mu.Unlock()
+ m.mu.RLock()
+ } else {
+ return &os.PathError{Op: "rename", Path: oldname, Err: ErrFileNotFound}
+ }
+ return nil
+}
+
+func (m *MemMapFs) renameDescendants(oldname, newname string) error {
+ descendants := m.findDescendants(oldname)
+ removes := make([]string, 0, len(descendants))
+ for _, desc := range descendants {
+ descNewName := strings.Replace(desc.Name(), oldname, newname, 1)
+ err := m.unRegisterWithParent(desc.Name())
+ if err != nil {
+ return err
+ }
+
+ removes = append(removes, desc.Name())
+ mem.ChangeFileName(desc, descNewName)
+ m.getData()[descNewName] = desc
+
+ m.registerWithParent(desc, 0)
+ }
+ for _, r := range removes {
+ delete(m.getData(), r)
+ }
+
+ return nil
+}
+
+func (m *MemMapFs) LstatIfPossible(name string) (os.FileInfo, bool, error) {
+ fileInfo, err := m.Stat(name)
+ return fileInfo, false, err
+}
+
+func (m *MemMapFs) Stat(name string) (os.FileInfo, error) {
+ f, err := m.Open(name)
+ if err != nil {
+ return nil, err
+ }
+ fi := mem.GetFileInfo(f.(*mem.File).Data())
+ return fi, nil
+}
+
+func (m *MemMapFs) Chmod(name string, mode os.FileMode) error {
+ mode &= chmodBits
+
+ m.mu.RLock()
+ f, ok := m.getData()[name]
+ m.mu.RUnlock()
+ if !ok {
+ return &os.PathError{Op: "chmod", Path: name, Err: ErrFileNotFound}
+ }
+ prevOtherBits := mem.GetFileInfo(f).Mode() & ^chmodBits
+
+ mode = prevOtherBits | mode
+ return m.setFileMode(name, mode)
+}
+
+func (m *MemMapFs) setFileMode(name string, mode os.FileMode) error {
+ name = normalizePath(name)
+
+ m.mu.RLock()
+ f, ok := m.getData()[name]
+ m.mu.RUnlock()
+ if !ok {
+ return &os.PathError{Op: "chmod", Path: name, Err: ErrFileNotFound}
+ }
+
+ m.mu.Lock()
+ mem.SetMode(f, mode)
+ m.mu.Unlock()
+
+ return nil
+}
+
+func (m *MemMapFs) Chown(name string, uid, gid int) error {
+ name = normalizePath(name)
+
+ m.mu.RLock()
+ f, ok := m.getData()[name]
+ m.mu.RUnlock()
+ if !ok {
+ return &os.PathError{Op: "chown", Path: name, Err: ErrFileNotFound}
+ }
+
+ mem.SetUID(f, uid)
+ mem.SetGID(f, gid)
+
+ return nil
+}
+
+func (m *MemMapFs) Chtimes(name string, atime time.Time, mtime time.Time) error {
+ name = normalizePath(name)
+
+ m.mu.RLock()
+ f, ok := m.getData()[name]
+ m.mu.RUnlock()
+ if !ok {
+ return &os.PathError{Op: "chtimes", Path: name, Err: ErrFileNotFound}
+ }
+
+ m.mu.Lock()
+ mem.SetModTime(f, mtime)
+ m.mu.Unlock()
+
+ return nil
+}
+
+func (m *MemMapFs) List() {
+ for _, x := range m.data {
+ y := mem.FileInfo{FileData: x}
+ fmt.Println(x.Name(), y.Size())
+ }
+}
diff --git a/vendor/github.com/spf13/afero/os.go b/vendor/github.com/spf13/afero/os.go
new file mode 100644
index 00000000..f1366321
--- /dev/null
+++ b/vendor/github.com/spf13/afero/os.go
@@ -0,0 +1,113 @@
+// Copyright © 2014 Steve Francia .
+// Copyright 2013 tsuru authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package afero
+
+import (
+ "os"
+ "time"
+)
+
+var _ Lstater = (*OsFs)(nil)
+
+// OsFs is a Fs implementation that uses functions provided by the os package.
+//
+// For details in any method, check the documentation of the os package
+// (http://golang.org/pkg/os/).
+type OsFs struct{}
+
+func NewOsFs() Fs {
+ return &OsFs{}
+}
+
+func (OsFs) Name() string { return "OsFs" }
+
+func (OsFs) Create(name string) (File, error) {
+ f, e := os.Create(name)
+ if f == nil {
+ // while this looks strange, we need to return a bare nil (of type nil) not
+ // a nil value of type *os.File or nil won't be nil
+ return nil, e
+ }
+ return f, e
+}
+
+func (OsFs) Mkdir(name string, perm os.FileMode) error {
+ return os.Mkdir(name, perm)
+}
+
+func (OsFs) MkdirAll(path string, perm os.FileMode) error {
+ return os.MkdirAll(path, perm)
+}
+
+func (OsFs) Open(name string) (File, error) {
+ f, e := os.Open(name)
+ if f == nil {
+ // while this looks strange, we need to return a bare nil (of type nil) not
+ // a nil value of type *os.File or nil won't be nil
+ return nil, e
+ }
+ return f, e
+}
+
+func (OsFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
+ f, e := os.OpenFile(name, flag, perm)
+ if f == nil {
+ // while this looks strange, we need to return a bare nil (of type nil) not
+ // a nil value of type *os.File or nil won't be nil
+ return nil, e
+ }
+ return f, e
+}
+
+func (OsFs) Remove(name string) error {
+ return os.Remove(name)
+}
+
+func (OsFs) RemoveAll(path string) error {
+ return os.RemoveAll(path)
+}
+
+func (OsFs) Rename(oldname, newname string) error {
+ return os.Rename(oldname, newname)
+}
+
+func (OsFs) Stat(name string) (os.FileInfo, error) {
+ return os.Stat(name)
+}
+
+func (OsFs) Chmod(name string, mode os.FileMode) error {
+ return os.Chmod(name, mode)
+}
+
+func (OsFs) Chown(name string, uid, gid int) error {
+ return os.Chown(name, uid, gid)
+}
+
+func (OsFs) Chtimes(name string, atime time.Time, mtime time.Time) error {
+ return os.Chtimes(name, atime, mtime)
+}
+
+func (OsFs) LstatIfPossible(name string) (os.FileInfo, bool, error) {
+ fi, err := os.Lstat(name)
+ return fi, true, err
+}
+
+func (OsFs) SymlinkIfPossible(oldname, newname string) error {
+ return os.Symlink(oldname, newname)
+}
+
+func (OsFs) ReadlinkIfPossible(name string) (string, error) {
+ return os.Readlink(name)
+}
diff --git a/vendor/github.com/spf13/afero/path.go b/vendor/github.com/spf13/afero/path.go
new file mode 100644
index 00000000..18f60a0f
--- /dev/null
+++ b/vendor/github.com/spf13/afero/path.go
@@ -0,0 +1,106 @@
+// Copyright ©2015 The Go Authors
+// Copyright ©2015 Steve Francia
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package afero
+
+import (
+ "os"
+ "path/filepath"
+ "sort"
+)
+
+// readDirNames reads the directory named by dirname and returns
+// a sorted list of directory entries.
+// adapted from https://golang.org/src/path/filepath/path.go
+func readDirNames(fs Fs, dirname string) ([]string, error) {
+ f, err := fs.Open(dirname)
+ if err != nil {
+ return nil, err
+ }
+ names, err := f.Readdirnames(-1)
+ f.Close()
+ if err != nil {
+ return nil, err
+ }
+ sort.Strings(names)
+ return names, nil
+}
+
+// walk recursively descends path, calling walkFn
+// adapted from https://golang.org/src/path/filepath/path.go
+func walk(fs Fs, path string, info os.FileInfo, walkFn filepath.WalkFunc) error {
+ err := walkFn(path, info, nil)
+ if err != nil {
+ if info.IsDir() && err == filepath.SkipDir {
+ return nil
+ }
+ return err
+ }
+
+ if !info.IsDir() {
+ return nil
+ }
+
+ names, err := readDirNames(fs, path)
+ if err != nil {
+ return walkFn(path, info, err)
+ }
+
+ for _, name := range names {
+ filename := filepath.Join(path, name)
+ fileInfo, err := lstatIfPossible(fs, filename)
+ if err != nil {
+ if err := walkFn(filename, fileInfo, err); err != nil && err != filepath.SkipDir {
+ return err
+ }
+ } else {
+ err = walk(fs, filename, fileInfo, walkFn)
+ if err != nil {
+ if !fileInfo.IsDir() || err != filepath.SkipDir {
+ return err
+ }
+ }
+ }
+ }
+ return nil
+}
+
+// if the filesystem supports it, use Lstat, else use fs.Stat
+func lstatIfPossible(fs Fs, path string) (os.FileInfo, error) {
+ if lfs, ok := fs.(Lstater); ok {
+ fi, _, err := lfs.LstatIfPossible(path)
+ return fi, err
+ }
+ return fs.Stat(path)
+}
+
+// Walk walks the file tree rooted at root, calling walkFn for each file or
+// directory in the tree, including root. All errors that arise visiting files
+// and directories are filtered by walkFn. The files are walked in lexical
+// order, which makes the output deterministic but means that for very
+// large directories Walk can be inefficient.
+// Walk does not follow symbolic links.
+
+func (a Afero) Walk(root string, walkFn filepath.WalkFunc) error {
+ return Walk(a.Fs, root, walkFn)
+}
+
+func Walk(fs Fs, root string, walkFn filepath.WalkFunc) error {
+ info, err := lstatIfPossible(fs, root)
+ if err != nil {
+ return walkFn(root, nil, err)
+ }
+ return walk(fs, root, info, walkFn)
+}
diff --git a/vendor/github.com/spf13/afero/readonlyfs.go b/vendor/github.com/spf13/afero/readonlyfs.go
new file mode 100644
index 00000000..bd8f9264
--- /dev/null
+++ b/vendor/github.com/spf13/afero/readonlyfs.go
@@ -0,0 +1,96 @@
+package afero
+
+import (
+ "os"
+ "syscall"
+ "time"
+)
+
+var _ Lstater = (*ReadOnlyFs)(nil)
+
+type ReadOnlyFs struct {
+ source Fs
+}
+
+func NewReadOnlyFs(source Fs) Fs {
+ return &ReadOnlyFs{source: source}
+}
+
+func (r *ReadOnlyFs) ReadDir(name string) ([]os.FileInfo, error) {
+ return ReadDir(r.source, name)
+}
+
+func (r *ReadOnlyFs) Chtimes(n string, a, m time.Time) error {
+ return syscall.EPERM
+}
+
+func (r *ReadOnlyFs) Chmod(n string, m os.FileMode) error {
+ return syscall.EPERM
+}
+
+func (r *ReadOnlyFs) Chown(n string, uid, gid int) error {
+ return syscall.EPERM
+}
+
+func (r *ReadOnlyFs) Name() string {
+ return "ReadOnlyFilter"
+}
+
+func (r *ReadOnlyFs) Stat(name string) (os.FileInfo, error) {
+ return r.source.Stat(name)
+}
+
+func (r *ReadOnlyFs) LstatIfPossible(name string) (os.FileInfo, bool, error) {
+ if lsf, ok := r.source.(Lstater); ok {
+ return lsf.LstatIfPossible(name)
+ }
+ fi, err := r.Stat(name)
+ return fi, false, err
+}
+
+func (r *ReadOnlyFs) SymlinkIfPossible(oldname, newname string) error {
+ return &os.LinkError{Op: "symlink", Old: oldname, New: newname, Err: ErrNoSymlink}
+}
+
+func (r *ReadOnlyFs) ReadlinkIfPossible(name string) (string, error) {
+ if srdr, ok := r.source.(LinkReader); ok {
+ return srdr.ReadlinkIfPossible(name)
+ }
+
+ return "", &os.PathError{Op: "readlink", Path: name, Err: ErrNoReadlink}
+}
+
+func (r *ReadOnlyFs) Rename(o, n string) error {
+ return syscall.EPERM
+}
+
+func (r *ReadOnlyFs) RemoveAll(p string) error {
+ return syscall.EPERM
+}
+
+func (r *ReadOnlyFs) Remove(n string) error {
+ return syscall.EPERM
+}
+
+func (r *ReadOnlyFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
+ if flag&(os.O_WRONLY|syscall.O_RDWR|os.O_APPEND|os.O_CREATE|os.O_TRUNC) != 0 {
+ return nil, syscall.EPERM
+ }
+ return r.source.OpenFile(name, flag, perm)
+}
+
+func (r *ReadOnlyFs) Open(n string) (File, error) {
+ return r.source.Open(n)
+}
+
+func (r *ReadOnlyFs) Mkdir(n string, p os.FileMode) error {
+ return syscall.EPERM
+}
+
+func (r *ReadOnlyFs) MkdirAll(n string, p os.FileMode) error {
+ return syscall.EPERM
+}
+
+func (r *ReadOnlyFs) Create(n string) (File, error) {
+ return nil, syscall.EPERM
+}
diff --git a/vendor/github.com/spf13/afero/regexpfs.go b/vendor/github.com/spf13/afero/regexpfs.go
new file mode 100644
index 00000000..218f3b23
--- /dev/null
+++ b/vendor/github.com/spf13/afero/regexpfs.go
@@ -0,0 +1,223 @@
+package afero
+
+import (
+ "os"
+ "regexp"
+ "syscall"
+ "time"
+)
+
+// The RegexpFs filters files (not directories) by regular expression. Only
+// files matching the given regexp will be allowed, all others get a ENOENT error (
+// "No such file or directory").
+type RegexpFs struct {
+ re *regexp.Regexp
+ source Fs
+}
+
+func NewRegexpFs(source Fs, re *regexp.Regexp) Fs {
+ return &RegexpFs{source: source, re: re}
+}
+
+type RegexpFile struct {
+ f File
+ re *regexp.Regexp
+}
+
+func (r *RegexpFs) matchesName(name string) error {
+ if r.re == nil {
+ return nil
+ }
+ if r.re.MatchString(name) {
+ return nil
+ }
+ return syscall.ENOENT
+}
+
+func (r *RegexpFs) dirOrMatches(name string) error {
+ dir, err := IsDir(r.source, name)
+ if err != nil {
+ return err
+ }
+ if dir {
+ return nil
+ }
+ return r.matchesName(name)
+}
+
+func (r *RegexpFs) Chtimes(name string, a, m time.Time) error {
+ if err := r.dirOrMatches(name); err != nil {
+ return err
+ }
+ return r.source.Chtimes(name, a, m)
+}
+
+func (r *RegexpFs) Chmod(name string, mode os.FileMode) error {
+ if err := r.dirOrMatches(name); err != nil {
+ return err
+ }
+ return r.source.Chmod(name, mode)
+}
+
+func (r *RegexpFs) Chown(name string, uid, gid int) error {
+ if err := r.dirOrMatches(name); err != nil {
+ return err
+ }
+ return r.source.Chown(name, uid, gid)
+}
+
+func (r *RegexpFs) Name() string {
+ return "RegexpFs"
+}
+
+func (r *RegexpFs) Stat(name string) (os.FileInfo, error) {
+ if err := r.dirOrMatches(name); err != nil {
+ return nil, err
+ }
+ return r.source.Stat(name)
+}
+
+func (r *RegexpFs) Rename(oldname, newname string) error {
+ dir, err := IsDir(r.source, oldname)
+ if err != nil {
+ return err
+ }
+ if dir {
+ return nil
+ }
+ if err := r.matchesName(oldname); err != nil {
+ return err
+ }
+ if err := r.matchesName(newname); err != nil {
+ return err
+ }
+ return r.source.Rename(oldname, newname)
+}
+
+func (r *RegexpFs) RemoveAll(p string) error {
+ dir, err := IsDir(r.source, p)
+ if err != nil {
+ return err
+ }
+ if !dir {
+ if err := r.matchesName(p); err != nil {
+ return err
+ }
+ }
+ return r.source.RemoveAll(p)
+}
+
+func (r *RegexpFs) Remove(name string) error {
+ if err := r.dirOrMatches(name); err != nil {
+ return err
+ }
+ return r.source.Remove(name)
+}
+
+func (r *RegexpFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
+ if err := r.dirOrMatches(name); err != nil {
+ return nil, err
+ }
+ return r.source.OpenFile(name, flag, perm)
+}
+
+func (r *RegexpFs) Open(name string) (File, error) {
+ dir, err := IsDir(r.source, name)
+ if err != nil {
+ return nil, err
+ }
+ if !dir {
+ if err := r.matchesName(name); err != nil {
+ return nil, err
+ }
+ }
+ f, err := r.source.Open(name)
+ if err != nil {
+ return nil, err
+ }
+ return &RegexpFile{f: f, re: r.re}, nil
+}
+
+func (r *RegexpFs) Mkdir(n string, p os.FileMode) error {
+ return r.source.Mkdir(n, p)
+}
+
+func (r *RegexpFs) MkdirAll(n string, p os.FileMode) error {
+ return r.source.MkdirAll(n, p)
+}
+
+func (r *RegexpFs) Create(name string) (File, error) {
+ if err := r.matchesName(name); err != nil {
+ return nil, err
+ }
+ return r.source.Create(name)
+}
+
+func (f *RegexpFile) Close() error {
+ return f.f.Close()
+}
+
+func (f *RegexpFile) Read(s []byte) (int, error) {
+ return f.f.Read(s)
+}
+
+func (f *RegexpFile) ReadAt(s []byte, o int64) (int, error) {
+ return f.f.ReadAt(s, o)
+}
+
+func (f *RegexpFile) Seek(o int64, w int) (int64, error) {
+ return f.f.Seek(o, w)
+}
+
+func (f *RegexpFile) Write(s []byte) (int, error) {
+ return f.f.Write(s)
+}
+
+func (f *RegexpFile) WriteAt(s []byte, o int64) (int, error) {
+ return f.f.WriteAt(s, o)
+}
+
+func (f *RegexpFile) Name() string {
+ return f.f.Name()
+}
+
+func (f *RegexpFile) Readdir(c int) (fi []os.FileInfo, err error) {
+ var rfi []os.FileInfo
+ rfi, err = f.f.Readdir(c)
+ if err != nil {
+ return nil, err
+ }
+ for _, i := range rfi {
+ if i.IsDir() || f.re.MatchString(i.Name()) {
+ fi = append(fi, i)
+ }
+ }
+ return fi, nil
+}
+
+func (f *RegexpFile) Readdirnames(c int) (n []string, err error) {
+ fi, err := f.Readdir(c)
+ if err != nil {
+ return nil, err
+ }
+ for _, s := range fi {
+ n = append(n, s.Name())
+ }
+ return n, nil
+}
+
+func (f *RegexpFile) Stat() (os.FileInfo, error) {
+ return f.f.Stat()
+}
+
+func (f *RegexpFile) Sync() error {
+ return f.f.Sync()
+}
+
+func (f *RegexpFile) Truncate(s int64) error {
+ return f.f.Truncate(s)
+}
+
+func (f *RegexpFile) WriteString(s string) (int, error) {
+ return f.f.WriteString(s)
+}
diff --git a/vendor/github.com/spf13/afero/symlink.go b/vendor/github.com/spf13/afero/symlink.go
new file mode 100644
index 00000000..aa6ae125
--- /dev/null
+++ b/vendor/github.com/spf13/afero/symlink.go
@@ -0,0 +1,55 @@
+// Copyright © 2018 Steve Francia .
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package afero
+
+import (
+ "errors"
+)
+
+// Symlinker is an optional interface in Afero. It is only implemented by the
+// filesystems saying so.
+// It indicates support for 3 symlink related interfaces that implement the
+// behaviors of the os methods:
+// - Lstat
+// - Symlink, and
+// - Readlink
+type Symlinker interface {
+ Lstater
+ Linker
+ LinkReader
+}
+
+// Linker is an optional interface in Afero. It is only implemented by the
+// filesystems saying so.
+// It will call Symlink if the filesystem itself is, or it delegates to, the os filesystem,
+// or the filesystem otherwise supports Symlink's.
+type Linker interface {
+ SymlinkIfPossible(oldname, newname string) error
+}
+
+// ErrNoSymlink is the error that will be wrapped in an os.LinkError if a file system
+// does not support Symlink's either directly or through its delegated filesystem.
+// As expressed by support for the Linker interface.
+var ErrNoSymlink = errors.New("symlink not supported")
+
+// LinkReader is an optional interface in Afero. It is only implemented by the
+// filesystems saying so.
+type LinkReader interface {
+ ReadlinkIfPossible(name string) (string, error)
+}
+
+// ErrNoReadlink is the error that will be wrapped in an os.Path if a file system
+// does not support the readlink operation either directly or through its delegated filesystem.
+// As expressed by support for the LinkReader interface.
+var ErrNoReadlink = errors.New("readlink not supported")
diff --git a/vendor/github.com/spf13/afero/unionFile.go b/vendor/github.com/spf13/afero/unionFile.go
new file mode 100644
index 00000000..62dd6c93
--- /dev/null
+++ b/vendor/github.com/spf13/afero/unionFile.go
@@ -0,0 +1,330 @@
+package afero
+
+import (
+ "io"
+ "os"
+ "path/filepath"
+ "syscall"
+)
+
+// The UnionFile implements the afero.File interface and will be returned
+// when reading a directory present at least in the overlay or opening a file
+// for writing.
+//
+// The calls to
+// Readdir() and Readdirnames() merge the file os.FileInfo / names from the
+// base and the overlay - for files present in both layers, only those
+// from the overlay will be used.
+//
+// When opening files for writing (Create() / OpenFile() with the right flags)
+// the operations will be done in both layers, starting with the overlay. A
+// successful read in the overlay will move the cursor position in the base layer
+// by the number of bytes read.
+type UnionFile struct {
+ Base File
+ Layer File
+ Merger DirsMerger
+ off int
+ files []os.FileInfo
+}
+
+func (f *UnionFile) Close() error {
+ // first close base, so we have a newer timestamp in the overlay. If we'd close
+ // the overlay first, we'd get a cacheStale the next time we access this file
+ // -> cache would be useless ;-)
+ if f.Base != nil {
+ f.Base.Close()
+ }
+ if f.Layer != nil {
+ return f.Layer.Close()
+ }
+ return BADFD
+}
+
+func (f *UnionFile) Read(s []byte) (int, error) {
+ if f.Layer != nil {
+ n, err := f.Layer.Read(s)
+ if (err == nil || err == io.EOF) && f.Base != nil {
+ // advance the file position also in the base file, the next
+ // call may be a write at this position (or a seek with SEEK_CUR)
+ if _, seekErr := f.Base.Seek(int64(n), io.SeekCurrent); seekErr != nil {
+ // only overwrite err in case the seek fails: we need to
+ // report an eventual io.EOF to the caller
+ err = seekErr
+ }
+ }
+ return n, err
+ }
+ if f.Base != nil {
+ return f.Base.Read(s)
+ }
+ return 0, BADFD
+}
+
+func (f *UnionFile) ReadAt(s []byte, o int64) (int, error) {
+ if f.Layer != nil {
+ n, err := f.Layer.ReadAt(s, o)
+ if (err == nil || err == io.EOF) && f.Base != nil {
+ _, err = f.Base.Seek(o+int64(n), io.SeekStart)
+ }
+ return n, err
+ }
+ if f.Base != nil {
+ return f.Base.ReadAt(s, o)
+ }
+ return 0, BADFD
+}
+
+func (f *UnionFile) Seek(o int64, w int) (pos int64, err error) {
+ if f.Layer != nil {
+ pos, err = f.Layer.Seek(o, w)
+ if (err == nil || err == io.EOF) && f.Base != nil {
+ _, err = f.Base.Seek(o, w)
+ }
+ return pos, err
+ }
+ if f.Base != nil {
+ return f.Base.Seek(o, w)
+ }
+ return 0, BADFD
+}
+
+func (f *UnionFile) Write(s []byte) (n int, err error) {
+ if f.Layer != nil {
+ n, err = f.Layer.Write(s)
+ if err == nil && f.Base != nil { // hmm, do we have fixed size files where a write may hit the EOF mark?
+ _, err = f.Base.Write(s)
+ }
+ return n, err
+ }
+ if f.Base != nil {
+ return f.Base.Write(s)
+ }
+ return 0, BADFD
+}
+
+func (f *UnionFile) WriteAt(s []byte, o int64) (n int, err error) {
+ if f.Layer != nil {
+ n, err = f.Layer.WriteAt(s, o)
+ if err == nil && f.Base != nil {
+ _, err = f.Base.WriteAt(s, o)
+ }
+ return n, err
+ }
+ if f.Base != nil {
+ return f.Base.WriteAt(s, o)
+ }
+ return 0, BADFD
+}
+
+func (f *UnionFile) Name() string {
+ if f.Layer != nil {
+ return f.Layer.Name()
+ }
+ return f.Base.Name()
+}
+
+// DirsMerger is how UnionFile weaves two directories together.
+// It takes the FileInfo slices from the layer and the base and returns a
+// single view.
+type DirsMerger func(lofi, bofi []os.FileInfo) ([]os.FileInfo, error)
+
+var defaultUnionMergeDirsFn = func(lofi, bofi []os.FileInfo) ([]os.FileInfo, error) {
+ files := make(map[string]os.FileInfo)
+
+ for _, fi := range lofi {
+ files[fi.Name()] = fi
+ }
+
+ for _, fi := range bofi {
+ if _, exists := files[fi.Name()]; !exists {
+ files[fi.Name()] = fi
+ }
+ }
+
+ rfi := make([]os.FileInfo, len(files))
+
+ i := 0
+ for _, fi := range files {
+ rfi[i] = fi
+ i++
+ }
+
+ return rfi, nil
+}
+
+// Readdir will weave the two directories together and
+// return a single view of the overlayed directories.
+// At the end of the directory view, the error is io.EOF if c > 0.
+func (f *UnionFile) Readdir(c int) (ofi []os.FileInfo, err error) {
+ var merge DirsMerger = f.Merger
+ if merge == nil {
+ merge = defaultUnionMergeDirsFn
+ }
+
+ if f.off == 0 {
+ var lfi []os.FileInfo
+ if f.Layer != nil {
+ lfi, err = f.Layer.Readdir(-1)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ var bfi []os.FileInfo
+ if f.Base != nil {
+ bfi, err = f.Base.Readdir(-1)
+ if err != nil {
+ return nil, err
+ }
+
+ }
+ merged, err := merge(lfi, bfi)
+ if err != nil {
+ return nil, err
+ }
+ f.files = append(f.files, merged...)
+ }
+ files := f.files[f.off:]
+
+ if c <= 0 {
+ return files, nil
+ }
+
+ if len(files) == 0 {
+ return nil, io.EOF
+ }
+
+ if c > len(files) {
+ c = len(files)
+ }
+
+ defer func() { f.off += c }()
+ return files[:c], nil
+}
+
+func (f *UnionFile) Readdirnames(c int) ([]string, error) {
+ rfi, err := f.Readdir(c)
+ if err != nil {
+ return nil, err
+ }
+ var names []string
+ for _, fi := range rfi {
+ names = append(names, fi.Name())
+ }
+ return names, nil
+}
+
+func (f *UnionFile) Stat() (os.FileInfo, error) {
+ if f.Layer != nil {
+ return f.Layer.Stat()
+ }
+ if f.Base != nil {
+ return f.Base.Stat()
+ }
+ return nil, BADFD
+}
+
+func (f *UnionFile) Sync() (err error) {
+ if f.Layer != nil {
+ err = f.Layer.Sync()
+ if err == nil && f.Base != nil {
+ err = f.Base.Sync()
+ }
+ return err
+ }
+ if f.Base != nil {
+ return f.Base.Sync()
+ }
+ return BADFD
+}
+
+func (f *UnionFile) Truncate(s int64) (err error) {
+ if f.Layer != nil {
+ err = f.Layer.Truncate(s)
+ if err == nil && f.Base != nil {
+ err = f.Base.Truncate(s)
+ }
+ return err
+ }
+ if f.Base != nil {
+ return f.Base.Truncate(s)
+ }
+ return BADFD
+}
+
+func (f *UnionFile) WriteString(s string) (n int, err error) {
+ if f.Layer != nil {
+ n, err = f.Layer.WriteString(s)
+ if err == nil && f.Base != nil {
+ _, err = f.Base.WriteString(s)
+ }
+ return n, err
+ }
+ if f.Base != nil {
+ return f.Base.WriteString(s)
+ }
+ return 0, BADFD
+}
+
+func copyFile(base Fs, layer Fs, name string, bfh File) error {
+ // First make sure the directory exists
+ exists, err := Exists(layer, filepath.Dir(name))
+ if err != nil {
+ return err
+ }
+ if !exists {
+ err = layer.MkdirAll(filepath.Dir(name), 0o777) // FIXME?
+ if err != nil {
+ return err
+ }
+ }
+
+ // Create the file on the overlay
+ lfh, err := layer.Create(name)
+ if err != nil {
+ return err
+ }
+ n, err := io.Copy(lfh, bfh)
+ if err != nil {
+ // If anything fails, clean up the file
+ layer.Remove(name)
+ lfh.Close()
+ return err
+ }
+
+ bfi, err := bfh.Stat()
+ if err != nil || bfi.Size() != n {
+ layer.Remove(name)
+ lfh.Close()
+ return syscall.EIO
+ }
+
+ err = lfh.Close()
+ if err != nil {
+ layer.Remove(name)
+ lfh.Close()
+ return err
+ }
+ return layer.Chtimes(name, bfi.ModTime(), bfi.ModTime())
+}
+
+func copyToLayer(base Fs, layer Fs, name string) error {
+ bfh, err := base.Open(name)
+ if err != nil {
+ return err
+ }
+ defer bfh.Close()
+
+ return copyFile(base, layer, name, bfh)
+}
+
+func copyFileToLayer(base Fs, layer Fs, name string, flag int, perm os.FileMode) error {
+ bfh, err := base.OpenFile(name, flag, perm)
+ if err != nil {
+ return err
+ }
+ defer bfh.Close()
+
+ return copyFile(base, layer, name, bfh)
+}
diff --git a/vendor/github.com/spf13/afero/util.go b/vendor/github.com/spf13/afero/util.go
new file mode 100644
index 00000000..9e4cba27
--- /dev/null
+++ b/vendor/github.com/spf13/afero/util.go
@@ -0,0 +1,329 @@
+// Copyright ©2015 Steve Francia
+// Portions Copyright ©2015 The Hugo Authors
+// Portions Copyright 2016-present Bjørn Erik Pedersen
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package afero
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "os"
+ "path/filepath"
+ "strings"
+ "unicode"
+
+ "golang.org/x/text/runes"
+ "golang.org/x/text/transform"
+ "golang.org/x/text/unicode/norm"
+)
+
+// Filepath separator defined by os.Separator.
+const FilePathSeparator = string(filepath.Separator)
+
+// Takes a reader and a path and writes the content
+func (a Afero) WriteReader(path string, r io.Reader) (err error) {
+ return WriteReader(a.Fs, path, r)
+}
+
+func WriteReader(fs Fs, path string, r io.Reader) (err error) {
+ dir, _ := filepath.Split(path)
+ ospath := filepath.FromSlash(dir)
+
+ if ospath != "" {
+ err = fs.MkdirAll(ospath, 0o777) // rwx, rw, r
+ if err != nil {
+ if err != os.ErrExist {
+ return err
+ }
+ }
+ }
+
+ file, err := fs.Create(path)
+ if err != nil {
+ return
+ }
+ defer file.Close()
+
+ _, err = io.Copy(file, r)
+ return
+}
+
+// Same as WriteReader but checks to see if file/directory already exists.
+func (a Afero) SafeWriteReader(path string, r io.Reader) (err error) {
+ return SafeWriteReader(a.Fs, path, r)
+}
+
+func SafeWriteReader(fs Fs, path string, r io.Reader) (err error) {
+ dir, _ := filepath.Split(path)
+ ospath := filepath.FromSlash(dir)
+
+ if ospath != "" {
+ err = fs.MkdirAll(ospath, 0o777) // rwx, rw, r
+ if err != nil {
+ return
+ }
+ }
+
+ exists, err := Exists(fs, path)
+ if err != nil {
+ return
+ }
+ if exists {
+ return fmt.Errorf("%v already exists", path)
+ }
+
+ file, err := fs.Create(path)
+ if err != nil {
+ return
+ }
+ defer file.Close()
+
+ _, err = io.Copy(file, r)
+ return
+}
+
+func (a Afero) GetTempDir(subPath string) string {
+ return GetTempDir(a.Fs, subPath)
+}
+
+// GetTempDir returns the default temp directory with trailing slash
+// if subPath is not empty then it will be created recursively with mode 777 rwx rwx rwx
+func GetTempDir(fs Fs, subPath string) string {
+ addSlash := func(p string) string {
+ if FilePathSeparator != p[len(p)-1:] {
+ p = p + FilePathSeparator
+ }
+ return p
+ }
+ dir := addSlash(os.TempDir())
+
+ if subPath != "" {
+ // preserve windows backslash :-(
+ if FilePathSeparator == "\\" {
+ subPath = strings.Replace(subPath, "\\", "____", -1)
+ }
+ dir = dir + UnicodeSanitize((subPath))
+ if FilePathSeparator == "\\" {
+ dir = strings.Replace(dir, "____", "\\", -1)
+ }
+
+ if exists, _ := Exists(fs, dir); exists {
+ return addSlash(dir)
+ }
+
+ err := fs.MkdirAll(dir, 0o777)
+ if err != nil {
+ panic(err)
+ }
+ dir = addSlash(dir)
+ }
+ return dir
+}
+
+// Rewrite string to remove non-standard path characters
+func UnicodeSanitize(s string) string {
+ source := []rune(s)
+ target := make([]rune, 0, len(source))
+
+ for _, r := range source {
+ if unicode.IsLetter(r) ||
+ unicode.IsDigit(r) ||
+ unicode.IsMark(r) ||
+ r == '.' ||
+ r == '/' ||
+ r == '\\' ||
+ r == '_' ||
+ r == '-' ||
+ r == '%' ||
+ r == ' ' ||
+ r == '#' {
+ target = append(target, r)
+ }
+ }
+
+ return string(target)
+}
+
+// Transform characters with accents into plain forms.
+func NeuterAccents(s string) string {
+ t := transform.Chain(norm.NFD, runes.Remove(runes.In(unicode.Mn)), norm.NFC)
+ result, _, _ := transform.String(t, string(s))
+
+ return result
+}
+
+func (a Afero) FileContainsBytes(filename string, subslice []byte) (bool, error) {
+ return FileContainsBytes(a.Fs, filename, subslice)
+}
+
+// Check if a file contains a specified byte slice.
+func FileContainsBytes(fs Fs, filename string, subslice []byte) (bool, error) {
+ f, err := fs.Open(filename)
+ if err != nil {
+ return false, err
+ }
+ defer f.Close()
+
+ return readerContainsAny(f, subslice), nil
+}
+
+func (a Afero) FileContainsAnyBytes(filename string, subslices [][]byte) (bool, error) {
+ return FileContainsAnyBytes(a.Fs, filename, subslices)
+}
+
+// Check if a file contains any of the specified byte slices.
+func FileContainsAnyBytes(fs Fs, filename string, subslices [][]byte) (bool, error) {
+ f, err := fs.Open(filename)
+ if err != nil {
+ return false, err
+ }
+ defer f.Close()
+
+ return readerContainsAny(f, subslices...), nil
+}
+
+// readerContains reports whether any of the subslices is within r.
+func readerContainsAny(r io.Reader, subslices ...[]byte) bool {
+ if r == nil || len(subslices) == 0 {
+ return false
+ }
+
+ largestSlice := 0
+
+ for _, sl := range subslices {
+ if len(sl) > largestSlice {
+ largestSlice = len(sl)
+ }
+ }
+
+ if largestSlice == 0 {
+ return false
+ }
+
+ bufflen := largestSlice * 4
+ halflen := bufflen / 2
+ buff := make([]byte, bufflen)
+ var err error
+ var n, i int
+
+ for {
+ i++
+ if i == 1 {
+ n, err = io.ReadAtLeast(r, buff[:halflen], halflen)
+ } else {
+ if i != 2 {
+ // shift left to catch overlapping matches
+ copy(buff[:], buff[halflen:])
+ }
+ n, err = io.ReadAtLeast(r, buff[halflen:], halflen)
+ }
+
+ if n > 0 {
+ for _, sl := range subslices {
+ if bytes.Contains(buff, sl) {
+ return true
+ }
+ }
+ }
+
+ if err != nil {
+ break
+ }
+ }
+ return false
+}
+
+func (a Afero) DirExists(path string) (bool, error) {
+ return DirExists(a.Fs, path)
+}
+
+// DirExists checks if a path exists and is a directory.
+func DirExists(fs Fs, path string) (bool, error) {
+ fi, err := fs.Stat(path)
+ if err == nil && fi.IsDir() {
+ return true, nil
+ }
+ if os.IsNotExist(err) {
+ return false, nil
+ }
+ return false, err
+}
+
+func (a Afero) IsDir(path string) (bool, error) {
+ return IsDir(a.Fs, path)
+}
+
+// IsDir checks if a given path is a directory.
+func IsDir(fs Fs, path string) (bool, error) {
+ fi, err := fs.Stat(path)
+ if err != nil {
+ return false, err
+ }
+ return fi.IsDir(), nil
+}
+
+func (a Afero) IsEmpty(path string) (bool, error) {
+ return IsEmpty(a.Fs, path)
+}
+
+// IsEmpty checks if a given file or directory is empty.
+func IsEmpty(fs Fs, path string) (bool, error) {
+ if b, _ := Exists(fs, path); !b {
+ return false, fmt.Errorf("%q path does not exist", path)
+ }
+ fi, err := fs.Stat(path)
+ if err != nil {
+ return false, err
+ }
+ if fi.IsDir() {
+ f, err := fs.Open(path)
+ if err != nil {
+ return false, err
+ }
+ defer f.Close()
+ list, err := f.Readdir(-1)
+ if err != nil {
+ return false, err
+ }
+ return len(list) == 0, nil
+ }
+ return fi.Size() == 0, nil
+}
+
+func (a Afero) Exists(path string) (bool, error) {
+ return Exists(a.Fs, path)
+}
+
+// Check if a file or directory exists.
+func Exists(fs Fs, path string) (bool, error) {
+ _, err := fs.Stat(path)
+ if err == nil {
+ return true, nil
+ }
+ if os.IsNotExist(err) {
+ return false, nil
+ }
+ return false, err
+}
+
+func FullBaseFsPath(basePathFs *BasePathFs, relativePath string) string {
+ combinedPath := filepath.Join(basePathFs.path, relativePath)
+ if parent, ok := basePathFs.source.(*BasePathFs); ok {
+ return FullBaseFsPath(parent, combinedPath)
+ }
+
+ return combinedPath
+}
diff --git a/vendor/github.com/spf13/cast/.gitignore b/vendor/github.com/spf13/cast/.gitignore
new file mode 100644
index 00000000..53053a8a
--- /dev/null
+++ b/vendor/github.com/spf13/cast/.gitignore
@@ -0,0 +1,25 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+
+*.bench
diff --git a/vendor/github.com/spf13/cast/LICENSE b/vendor/github.com/spf13/cast/LICENSE
new file mode 100644
index 00000000..4527efb9
--- /dev/null
+++ b/vendor/github.com/spf13/cast/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Steve Francia
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/vendor/github.com/spf13/cast/Makefile b/vendor/github.com/spf13/cast/Makefile
new file mode 100644
index 00000000..f01a5dbb
--- /dev/null
+++ b/vendor/github.com/spf13/cast/Makefile
@@ -0,0 +1,40 @@
+GOVERSION := $(shell go version | cut -d ' ' -f 3 | cut -d '.' -f 2)
+
+.PHONY: check fmt lint test test-race vet test-cover-html help
+.DEFAULT_GOAL := help
+
+check: test-race fmt vet lint ## Run tests and linters
+
+test: ## Run tests
+ go test ./...
+
+test-race: ## Run tests with race detector
+ go test -race ./...
+
+fmt: ## Run gofmt linter
+ifeq "$(GOVERSION)" "12"
+ @for d in `go list` ; do \
+ if [ "`gofmt -l -s $$GOPATH/src/$$d | tee /dev/stderr`" ]; then \
+ echo "^ improperly formatted go files" && echo && exit 1; \
+ fi \
+ done
+endif
+
+lint: ## Run golint linter
+ @for d in `go list` ; do \
+ if [ "`golint $$d | tee /dev/stderr`" ]; then \
+ echo "^ golint errors!" && echo && exit 1; \
+ fi \
+ done
+
+vet: ## Run go vet linter
+ @if [ "`go vet | tee /dev/stderr`" ]; then \
+ echo "^ go vet errors!" && echo && exit 1; \
+ fi
+
+test-cover-html: ## Generate test coverage report
+ go test -coverprofile=coverage.out -covermode=count
+ go tool cover -func=coverage.out
+
+help:
+ @grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
diff --git a/vendor/github.com/spf13/cast/README.md b/vendor/github.com/spf13/cast/README.md
new file mode 100644
index 00000000..0e9e1459
--- /dev/null
+++ b/vendor/github.com/spf13/cast/README.md
@@ -0,0 +1,75 @@
+# cast
+
+[](https://github.com/spf13/cast/actions/workflows/ci.yaml)
+[](https://pkg.go.dev/mod/github.com/spf13/cast)
+
+[](https://goreportcard.com/report/github.com/spf13/cast)
+
+Easy and safe casting from one type to another in Go
+
+Don’t Panic! ... Cast
+
+## What is Cast?
+
+Cast is a library to convert between different go types in a consistent and easy way.
+
+Cast provides simple functions to easily convert a number to a string, an
+interface into a bool, etc. Cast does this intelligently when an obvious
+conversion is possible. It doesn’t make any attempts to guess what you meant,
+for example you can only convert a string to an int when it is a string
+representation of an int such as “8”. Cast was developed for use in
+[Hugo](https://gohugo.io), a website engine which uses YAML, TOML or JSON
+for meta data.
+
+## Why use Cast?
+
+When working with dynamic data in Go you often need to cast or convert the data
+from one type into another. Cast goes beyond just using type assertion (though
+it uses that when possible) to provide a very straightforward and convenient
+library.
+
+If you are working with interfaces to handle things like dynamic content
+you’ll need an easy way to convert an interface into a given type. This
+is the library for you.
+
+If you are taking in data from YAML, TOML or JSON or other formats which lack
+full types, then Cast is the library for you.
+
+## Usage
+
+Cast provides a handful of To_____ methods. These methods will always return
+the desired type. **If input is provided that will not convert to that type, the
+0 or nil value for that type will be returned**.
+
+Cast also provides identical methods To_____E. These return the same result as
+the To_____ methods, plus an additional error which tells you if it successfully
+converted. Using these methods you can tell the difference between when the
+input matched the zero value or when the conversion failed and the zero value
+was returned.
+
+The following examples are merely a sample of what is available. Please review
+the code for a complete set.
+
+### Example ‘ToString’:
+
+ cast.ToString("mayonegg") // "mayonegg"
+ cast.ToString(8) // "8"
+ cast.ToString(8.31) // "8.31"
+ cast.ToString([]byte("one time")) // "one time"
+ cast.ToString(nil) // ""
+
+ var foo interface{} = "one more time"
+ cast.ToString(foo) // "one more time"
+
+
+### Example ‘ToInt’:
+
+ cast.ToInt(8) // 8
+ cast.ToInt(8.31) // 8
+ cast.ToInt("8") // 8
+ cast.ToInt(true) // 1
+ cast.ToInt(false) // 0
+
+ var eight interface{} = 8
+ cast.ToInt(eight) // 8
+ cast.ToInt(nil) // 0
diff --git a/vendor/github.com/spf13/cast/cast.go b/vendor/github.com/spf13/cast/cast.go
new file mode 100644
index 00000000..0cfe9418
--- /dev/null
+++ b/vendor/github.com/spf13/cast/cast.go
@@ -0,0 +1,176 @@
+// Copyright © 2014 Steve Francia .
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file.
+
+// Package cast provides easy and safe casting in Go.
+package cast
+
+import "time"
+
+// ToBool casts an interface to a bool type.
+func ToBool(i interface{}) bool {
+ v, _ := ToBoolE(i)
+ return v
+}
+
+// ToTime casts an interface to a time.Time type.
+func ToTime(i interface{}) time.Time {
+ v, _ := ToTimeE(i)
+ return v
+}
+
+func ToTimeInDefaultLocation(i interface{}, location *time.Location) time.Time {
+ v, _ := ToTimeInDefaultLocationE(i, location)
+ return v
+}
+
+// ToDuration casts an interface to a time.Duration type.
+func ToDuration(i interface{}) time.Duration {
+ v, _ := ToDurationE(i)
+ return v
+}
+
+// ToFloat64 casts an interface to a float64 type.
+func ToFloat64(i interface{}) float64 {
+ v, _ := ToFloat64E(i)
+ return v
+}
+
+// ToFloat32 casts an interface to a float32 type.
+func ToFloat32(i interface{}) float32 {
+ v, _ := ToFloat32E(i)
+ return v
+}
+
+// ToInt64 casts an interface to an int64 type.
+func ToInt64(i interface{}) int64 {
+ v, _ := ToInt64E(i)
+ return v
+}
+
+// ToInt32 casts an interface to an int32 type.
+func ToInt32(i interface{}) int32 {
+ v, _ := ToInt32E(i)
+ return v
+}
+
+// ToInt16 casts an interface to an int16 type.
+func ToInt16(i interface{}) int16 {
+ v, _ := ToInt16E(i)
+ return v
+}
+
+// ToInt8 casts an interface to an int8 type.
+func ToInt8(i interface{}) int8 {
+ v, _ := ToInt8E(i)
+ return v
+}
+
+// ToInt casts an interface to an int type.
+func ToInt(i interface{}) int {
+ v, _ := ToIntE(i)
+ return v
+}
+
+// ToUint casts an interface to a uint type.
+func ToUint(i interface{}) uint {
+ v, _ := ToUintE(i)
+ return v
+}
+
+// ToUint64 casts an interface to a uint64 type.
+func ToUint64(i interface{}) uint64 {
+ v, _ := ToUint64E(i)
+ return v
+}
+
+// ToUint32 casts an interface to a uint32 type.
+func ToUint32(i interface{}) uint32 {
+ v, _ := ToUint32E(i)
+ return v
+}
+
+// ToUint16 casts an interface to a uint16 type.
+func ToUint16(i interface{}) uint16 {
+ v, _ := ToUint16E(i)
+ return v
+}
+
+// ToUint8 casts an interface to a uint8 type.
+func ToUint8(i interface{}) uint8 {
+ v, _ := ToUint8E(i)
+ return v
+}
+
+// ToString casts an interface to a string type.
+func ToString(i interface{}) string {
+ v, _ := ToStringE(i)
+ return v
+}
+
+// ToStringMapString casts an interface to a map[string]string type.
+func ToStringMapString(i interface{}) map[string]string {
+ v, _ := ToStringMapStringE(i)
+ return v
+}
+
+// ToStringMapStringSlice casts an interface to a map[string][]string type.
+func ToStringMapStringSlice(i interface{}) map[string][]string {
+ v, _ := ToStringMapStringSliceE(i)
+ return v
+}
+
+// ToStringMapBool casts an interface to a map[string]bool type.
+func ToStringMapBool(i interface{}) map[string]bool {
+ v, _ := ToStringMapBoolE(i)
+ return v
+}
+
+// ToStringMapInt casts an interface to a map[string]int type.
+func ToStringMapInt(i interface{}) map[string]int {
+ v, _ := ToStringMapIntE(i)
+ return v
+}
+
+// ToStringMapInt64 casts an interface to a map[string]int64 type.
+func ToStringMapInt64(i interface{}) map[string]int64 {
+ v, _ := ToStringMapInt64E(i)
+ return v
+}
+
+// ToStringMap casts an interface to a map[string]interface{} type.
+func ToStringMap(i interface{}) map[string]interface{} {
+ v, _ := ToStringMapE(i)
+ return v
+}
+
+// ToSlice casts an interface to a []interface{} type.
+func ToSlice(i interface{}) []interface{} {
+ v, _ := ToSliceE(i)
+ return v
+}
+
+// ToBoolSlice casts an interface to a []bool type.
+func ToBoolSlice(i interface{}) []bool {
+ v, _ := ToBoolSliceE(i)
+ return v
+}
+
+// ToStringSlice casts an interface to a []string type.
+func ToStringSlice(i interface{}) []string {
+ v, _ := ToStringSliceE(i)
+ return v
+}
+
+// ToIntSlice casts an interface to a []int type.
+func ToIntSlice(i interface{}) []int {
+ v, _ := ToIntSliceE(i)
+ return v
+}
+
+// ToDurationSlice casts an interface to a []time.Duration type.
+func ToDurationSlice(i interface{}) []time.Duration {
+ v, _ := ToDurationSliceE(i)
+ return v
+}
diff --git a/vendor/github.com/spf13/cast/caste.go b/vendor/github.com/spf13/cast/caste.go
new file mode 100644
index 00000000..d49bbf83
--- /dev/null
+++ b/vendor/github.com/spf13/cast/caste.go
@@ -0,0 +1,1498 @@
+// Copyright © 2014 Steve Francia .
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file.
+
+package cast
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "html/template"
+ "reflect"
+ "strconv"
+ "strings"
+ "time"
+)
+
+var errNegativeNotAllowed = errors.New("unable to cast negative value")
+
+// ToTimeE casts an interface to a time.Time type.
+func ToTimeE(i interface{}) (tim time.Time, err error) {
+ return ToTimeInDefaultLocationE(i, time.UTC)
+}
+
+// ToTimeInDefaultLocationE casts an empty interface to time.Time,
+// interpreting inputs without a timezone to be in the given location,
+// or the local timezone if nil.
+func ToTimeInDefaultLocationE(i interface{}, location *time.Location) (tim time.Time, err error) {
+ i = indirect(i)
+
+ switch v := i.(type) {
+ case time.Time:
+ return v, nil
+ case string:
+ return StringToDateInDefaultLocation(v, location)
+ case json.Number:
+ s, err1 := ToInt64E(v)
+ if err1 != nil {
+ return time.Time{}, fmt.Errorf("unable to cast %#v of type %T to Time", i, i)
+ }
+ return time.Unix(s, 0), nil
+ case int:
+ return time.Unix(int64(v), 0), nil
+ case int64:
+ return time.Unix(v, 0), nil
+ case int32:
+ return time.Unix(int64(v), 0), nil
+ case uint:
+ return time.Unix(int64(v), 0), nil
+ case uint64:
+ return time.Unix(int64(v), 0), nil
+ case uint32:
+ return time.Unix(int64(v), 0), nil
+ default:
+ return time.Time{}, fmt.Errorf("unable to cast %#v of type %T to Time", i, i)
+ }
+}
+
+// ToDurationE casts an interface to a time.Duration type.
+func ToDurationE(i interface{}) (d time.Duration, err error) {
+ i = indirect(i)
+
+ switch s := i.(type) {
+ case time.Duration:
+ return s, nil
+ case int, int64, int32, int16, int8, uint, uint64, uint32, uint16, uint8:
+ d = time.Duration(ToInt64(s))
+ return
+ case float32, float64:
+ d = time.Duration(ToFloat64(s))
+ return
+ case string:
+ if strings.ContainsAny(s, "nsuµmh") {
+ d, err = time.ParseDuration(s)
+ } else {
+ d, err = time.ParseDuration(s + "ns")
+ }
+ return
+ case json.Number:
+ var v float64
+ v, err = s.Float64()
+ d = time.Duration(v)
+ return
+ default:
+ err = fmt.Errorf("unable to cast %#v of type %T to Duration", i, i)
+ return
+ }
+}
+
+// ToBoolE casts an interface to a bool type.
+func ToBoolE(i interface{}) (bool, error) {
+ i = indirect(i)
+
+ switch b := i.(type) {
+ case bool:
+ return b, nil
+ case nil:
+ return false, nil
+ case int:
+ return b != 0, nil
+ case int64:
+ return b != 0, nil
+ case int32:
+ return b != 0, nil
+ case int16:
+ return b != 0, nil
+ case int8:
+ return b != 0, nil
+ case uint:
+ return b != 0, nil
+ case uint64:
+ return b != 0, nil
+ case uint32:
+ return b != 0, nil
+ case uint16:
+ return b != 0, nil
+ case uint8:
+ return b != 0, nil
+ case float64:
+ return b != 0, nil
+ case float32:
+ return b != 0, nil
+ case time.Duration:
+ return b != 0, nil
+ case string:
+ return strconv.ParseBool(i.(string))
+ case json.Number:
+ v, err := ToInt64E(b)
+ if err == nil {
+ return v != 0, nil
+ }
+ return false, fmt.Errorf("unable to cast %#v of type %T to bool", i, i)
+ default:
+ return false, fmt.Errorf("unable to cast %#v of type %T to bool", i, i)
+ }
+}
+
+// ToFloat64E casts an interface to a float64 type.
+func ToFloat64E(i interface{}) (float64, error) {
+ i = indirect(i)
+
+ intv, ok := toInt(i)
+ if ok {
+ return float64(intv), nil
+ }
+
+ switch s := i.(type) {
+ case float64:
+ return s, nil
+ case float32:
+ return float64(s), nil
+ case int64:
+ return float64(s), nil
+ case int32:
+ return float64(s), nil
+ case int16:
+ return float64(s), nil
+ case int8:
+ return float64(s), nil
+ case uint:
+ return float64(s), nil
+ case uint64:
+ return float64(s), nil
+ case uint32:
+ return float64(s), nil
+ case uint16:
+ return float64(s), nil
+ case uint8:
+ return float64(s), nil
+ case string:
+ v, err := strconv.ParseFloat(s, 64)
+ if err == nil {
+ return v, nil
+ }
+ return 0, fmt.Errorf("unable to cast %#v of type %T to float64", i, i)
+ case json.Number:
+ v, err := s.Float64()
+ if err == nil {
+ return v, nil
+ }
+ return 0, fmt.Errorf("unable to cast %#v of type %T to float64", i, i)
+ case bool:
+ if s {
+ return 1, nil
+ }
+ return 0, nil
+ case nil:
+ return 0, nil
+ default:
+ return 0, fmt.Errorf("unable to cast %#v of type %T to float64", i, i)
+ }
+}
+
+// ToFloat32E casts an interface to a float32 type.
+func ToFloat32E(i interface{}) (float32, error) {
+ i = indirect(i)
+
+ intv, ok := toInt(i)
+ if ok {
+ return float32(intv), nil
+ }
+
+ switch s := i.(type) {
+ case float64:
+ return float32(s), nil
+ case float32:
+ return s, nil
+ case int64:
+ return float32(s), nil
+ case int32:
+ return float32(s), nil
+ case int16:
+ return float32(s), nil
+ case int8:
+ return float32(s), nil
+ case uint:
+ return float32(s), nil
+ case uint64:
+ return float32(s), nil
+ case uint32:
+ return float32(s), nil
+ case uint16:
+ return float32(s), nil
+ case uint8:
+ return float32(s), nil
+ case string:
+ v, err := strconv.ParseFloat(s, 32)
+ if err == nil {
+ return float32(v), nil
+ }
+ return 0, fmt.Errorf("unable to cast %#v of type %T to float32", i, i)
+ case json.Number:
+ v, err := s.Float64()
+ if err == nil {
+ return float32(v), nil
+ }
+ return 0, fmt.Errorf("unable to cast %#v of type %T to float32", i, i)
+ case bool:
+ if s {
+ return 1, nil
+ }
+ return 0, nil
+ case nil:
+ return 0, nil
+ default:
+ return 0, fmt.Errorf("unable to cast %#v of type %T to float32", i, i)
+ }
+}
+
+// ToInt64E casts an interface to an int64 type.
+func ToInt64E(i interface{}) (int64, error) {
+ i = indirect(i)
+
+ intv, ok := toInt(i)
+ if ok {
+ return int64(intv), nil
+ }
+
+ switch s := i.(type) {
+ case int64:
+ return s, nil
+ case int32:
+ return int64(s), nil
+ case int16:
+ return int64(s), nil
+ case int8:
+ return int64(s), nil
+ case uint:
+ return int64(s), nil
+ case uint64:
+ return int64(s), nil
+ case uint32:
+ return int64(s), nil
+ case uint16:
+ return int64(s), nil
+ case uint8:
+ return int64(s), nil
+ case float64:
+ return int64(s), nil
+ case float32:
+ return int64(s), nil
+ case string:
+ v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0)
+ if err == nil {
+ return v, nil
+ }
+ return 0, fmt.Errorf("unable to cast %#v of type %T to int64", i, i)
+ case json.Number:
+ return ToInt64E(string(s))
+ case bool:
+ if s {
+ return 1, nil
+ }
+ return 0, nil
+ case nil:
+ return 0, nil
+ default:
+ return 0, fmt.Errorf("unable to cast %#v of type %T to int64", i, i)
+ }
+}
+
+// ToInt32E casts an interface to an int32 type.
+func ToInt32E(i interface{}) (int32, error) {
+ i = indirect(i)
+
+ intv, ok := toInt(i)
+ if ok {
+ return int32(intv), nil
+ }
+
+ switch s := i.(type) {
+ case int64:
+ return int32(s), nil
+ case int32:
+ return s, nil
+ case int16:
+ return int32(s), nil
+ case int8:
+ return int32(s), nil
+ case uint:
+ return int32(s), nil
+ case uint64:
+ return int32(s), nil
+ case uint32:
+ return int32(s), nil
+ case uint16:
+ return int32(s), nil
+ case uint8:
+ return int32(s), nil
+ case float64:
+ return int32(s), nil
+ case float32:
+ return int32(s), nil
+ case string:
+ v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0)
+ if err == nil {
+ return int32(v), nil
+ }
+ return 0, fmt.Errorf("unable to cast %#v of type %T to int32", i, i)
+ case json.Number:
+ return ToInt32E(string(s))
+ case bool:
+ if s {
+ return 1, nil
+ }
+ return 0, nil
+ case nil:
+ return 0, nil
+ default:
+ return 0, fmt.Errorf("unable to cast %#v of type %T to int32", i, i)
+ }
+}
+
+// ToInt16E casts an interface to an int16 type.
+func ToInt16E(i interface{}) (int16, error) {
+ i = indirect(i)
+
+ intv, ok := toInt(i)
+ if ok {
+ return int16(intv), nil
+ }
+
+ switch s := i.(type) {
+ case int64:
+ return int16(s), nil
+ case int32:
+ return int16(s), nil
+ case int16:
+ return s, nil
+ case int8:
+ return int16(s), nil
+ case uint:
+ return int16(s), nil
+ case uint64:
+ return int16(s), nil
+ case uint32:
+ return int16(s), nil
+ case uint16:
+ return int16(s), nil
+ case uint8:
+ return int16(s), nil
+ case float64:
+ return int16(s), nil
+ case float32:
+ return int16(s), nil
+ case string:
+ v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0)
+ if err == nil {
+ return int16(v), nil
+ }
+ return 0, fmt.Errorf("unable to cast %#v of type %T to int16", i, i)
+ case json.Number:
+ return ToInt16E(string(s))
+ case bool:
+ if s {
+ return 1, nil
+ }
+ return 0, nil
+ case nil:
+ return 0, nil
+ default:
+ return 0, fmt.Errorf("unable to cast %#v of type %T to int16", i, i)
+ }
+}
+
+// ToInt8E casts an interface to an int8 type.
+func ToInt8E(i interface{}) (int8, error) {
+ i = indirect(i)
+
+ intv, ok := toInt(i)
+ if ok {
+ return int8(intv), nil
+ }
+
+ switch s := i.(type) {
+ case int64:
+ return int8(s), nil
+ case int32:
+ return int8(s), nil
+ case int16:
+ return int8(s), nil
+ case int8:
+ return s, nil
+ case uint:
+ return int8(s), nil
+ case uint64:
+ return int8(s), nil
+ case uint32:
+ return int8(s), nil
+ case uint16:
+ return int8(s), nil
+ case uint8:
+ return int8(s), nil
+ case float64:
+ return int8(s), nil
+ case float32:
+ return int8(s), nil
+ case string:
+ v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0)
+ if err == nil {
+ return int8(v), nil
+ }
+ return 0, fmt.Errorf("unable to cast %#v of type %T to int8", i, i)
+ case json.Number:
+ return ToInt8E(string(s))
+ case bool:
+ if s {
+ return 1, nil
+ }
+ return 0, nil
+ case nil:
+ return 0, nil
+ default:
+ return 0, fmt.Errorf("unable to cast %#v of type %T to int8", i, i)
+ }
+}
+
+// ToIntE casts an interface to an int type.
+func ToIntE(i interface{}) (int, error) {
+ i = indirect(i)
+
+ intv, ok := toInt(i)
+ if ok {
+ return intv, nil
+ }
+
+ switch s := i.(type) {
+ case int64:
+ return int(s), nil
+ case int32:
+ return int(s), nil
+ case int16:
+ return int(s), nil
+ case int8:
+ return int(s), nil
+ case uint:
+ return int(s), nil
+ case uint64:
+ return int(s), nil
+ case uint32:
+ return int(s), nil
+ case uint16:
+ return int(s), nil
+ case uint8:
+ return int(s), nil
+ case float64:
+ return int(s), nil
+ case float32:
+ return int(s), nil
+ case string:
+ v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0)
+ if err == nil {
+ return int(v), nil
+ }
+ return 0, fmt.Errorf("unable to cast %#v of type %T to int64", i, i)
+ case json.Number:
+ return ToIntE(string(s))
+ case bool:
+ if s {
+ return 1, nil
+ }
+ return 0, nil
+ case nil:
+ return 0, nil
+ default:
+ return 0, fmt.Errorf("unable to cast %#v of type %T to int", i, i)
+ }
+}
+
+// ToUintE casts an interface to a uint type.
+func ToUintE(i interface{}) (uint, error) {
+ i = indirect(i)
+
+ intv, ok := toInt(i)
+ if ok {
+ if intv < 0 {
+ return 0, errNegativeNotAllowed
+ }
+ return uint(intv), nil
+ }
+
+ switch s := i.(type) {
+ case string:
+ v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0)
+ if err == nil {
+ if v < 0 {
+ return 0, errNegativeNotAllowed
+ }
+ return uint(v), nil
+ }
+ return 0, fmt.Errorf("unable to cast %#v of type %T to uint", i, i)
+ case json.Number:
+ return ToUintE(string(s))
+ case int64:
+ if s < 0 {
+ return 0, errNegativeNotAllowed
+ }
+ return uint(s), nil
+ case int32:
+ if s < 0 {
+ return 0, errNegativeNotAllowed
+ }
+ return uint(s), nil
+ case int16:
+ if s < 0 {
+ return 0, errNegativeNotAllowed
+ }
+ return uint(s), nil
+ case int8:
+ if s < 0 {
+ return 0, errNegativeNotAllowed
+ }
+ return uint(s), nil
+ case uint:
+ return s, nil
+ case uint64:
+ return uint(s), nil
+ case uint32:
+ return uint(s), nil
+ case uint16:
+ return uint(s), nil
+ case uint8:
+ return uint(s), nil
+ case float64:
+ if s < 0 {
+ return 0, errNegativeNotAllowed
+ }
+ return uint(s), nil
+ case float32:
+ if s < 0 {
+ return 0, errNegativeNotAllowed
+ }
+ return uint(s), nil
+ case bool:
+ if s {
+ return 1, nil
+ }
+ return 0, nil
+ case nil:
+ return 0, nil
+ default:
+ return 0, fmt.Errorf("unable to cast %#v of type %T to uint", i, i)
+ }
+}
+
+// ToUint64E casts an interface to a uint64 type.
+func ToUint64E(i interface{}) (uint64, error) {
+ i = indirect(i)
+
+ intv, ok := toInt(i)
+ if ok {
+ if intv < 0 {
+ return 0, errNegativeNotAllowed
+ }
+ return uint64(intv), nil
+ }
+
+ switch s := i.(type) {
+ case string:
+ v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0)
+ if err == nil {
+ if v < 0 {
+ return 0, errNegativeNotAllowed
+ }
+ return uint64(v), nil
+ }
+ return 0, fmt.Errorf("unable to cast %#v of type %T to uint64", i, i)
+ case json.Number:
+ return ToUint64E(string(s))
+ case int64:
+ if s < 0 {
+ return 0, errNegativeNotAllowed
+ }
+ return uint64(s), nil
+ case int32:
+ if s < 0 {
+ return 0, errNegativeNotAllowed
+ }
+ return uint64(s), nil
+ case int16:
+ if s < 0 {
+ return 0, errNegativeNotAllowed
+ }
+ return uint64(s), nil
+ case int8:
+ if s < 0 {
+ return 0, errNegativeNotAllowed
+ }
+ return uint64(s), nil
+ case uint:
+ return uint64(s), nil
+ case uint64:
+ return s, nil
+ case uint32:
+ return uint64(s), nil
+ case uint16:
+ return uint64(s), nil
+ case uint8:
+ return uint64(s), nil
+ case float32:
+ if s < 0 {
+ return 0, errNegativeNotAllowed
+ }
+ return uint64(s), nil
+ case float64:
+ if s < 0 {
+ return 0, errNegativeNotAllowed
+ }
+ return uint64(s), nil
+ case bool:
+ if s {
+ return 1, nil
+ }
+ return 0, nil
+ case nil:
+ return 0, nil
+ default:
+ return 0, fmt.Errorf("unable to cast %#v of type %T to uint64", i, i)
+ }
+}
+
+// ToUint32E casts an interface to a uint32 type.
+func ToUint32E(i interface{}) (uint32, error) {
+ i = indirect(i)
+
+ intv, ok := toInt(i)
+ if ok {
+ if intv < 0 {
+ return 0, errNegativeNotAllowed
+ }
+ return uint32(intv), nil
+ }
+
+ switch s := i.(type) {
+ case string:
+ v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0)
+ if err == nil {
+ if v < 0 {
+ return 0, errNegativeNotAllowed
+ }
+ return uint32(v), nil
+ }
+ return 0, fmt.Errorf("unable to cast %#v of type %T to uint32", i, i)
+ case json.Number:
+ return ToUint32E(string(s))
+ case int64:
+ if s < 0 {
+ return 0, errNegativeNotAllowed
+ }
+ return uint32(s), nil
+ case int32:
+ if s < 0 {
+ return 0, errNegativeNotAllowed
+ }
+ return uint32(s), nil
+ case int16:
+ if s < 0 {
+ return 0, errNegativeNotAllowed
+ }
+ return uint32(s), nil
+ case int8:
+ if s < 0 {
+ return 0, errNegativeNotAllowed
+ }
+ return uint32(s), nil
+ case uint:
+ return uint32(s), nil
+ case uint64:
+ return uint32(s), nil
+ case uint32:
+ return s, nil
+ case uint16:
+ return uint32(s), nil
+ case uint8:
+ return uint32(s), nil
+ case float64:
+ if s < 0 {
+ return 0, errNegativeNotAllowed
+ }
+ return uint32(s), nil
+ case float32:
+ if s < 0 {
+ return 0, errNegativeNotAllowed
+ }
+ return uint32(s), nil
+ case bool:
+ if s {
+ return 1, nil
+ }
+ return 0, nil
+ case nil:
+ return 0, nil
+ default:
+ return 0, fmt.Errorf("unable to cast %#v of type %T to uint32", i, i)
+ }
+}
+
+// ToUint16E casts an interface to a uint16 type.
+func ToUint16E(i interface{}) (uint16, error) {
+ i = indirect(i)
+
+ intv, ok := toInt(i)
+ if ok {
+ if intv < 0 {
+ return 0, errNegativeNotAllowed
+ }
+ return uint16(intv), nil
+ }
+
+ switch s := i.(type) {
+ case string:
+ v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0)
+ if err == nil {
+ if v < 0 {
+ return 0, errNegativeNotAllowed
+ }
+ return uint16(v), nil
+ }
+ return 0, fmt.Errorf("unable to cast %#v of type %T to uint16", i, i)
+ case json.Number:
+ return ToUint16E(string(s))
+ case int64:
+ if s < 0 {
+ return 0, errNegativeNotAllowed
+ }
+ return uint16(s), nil
+ case int32:
+ if s < 0 {
+ return 0, errNegativeNotAllowed
+ }
+ return uint16(s), nil
+ case int16:
+ if s < 0 {
+ return 0, errNegativeNotAllowed
+ }
+ return uint16(s), nil
+ case int8:
+ if s < 0 {
+ return 0, errNegativeNotAllowed
+ }
+ return uint16(s), nil
+ case uint:
+ return uint16(s), nil
+ case uint64:
+ return uint16(s), nil
+ case uint32:
+ return uint16(s), nil
+ case uint16:
+ return s, nil
+ case uint8:
+ return uint16(s), nil
+ case float64:
+ if s < 0 {
+ return 0, errNegativeNotAllowed
+ }
+ return uint16(s), nil
+ case float32:
+ if s < 0 {
+ return 0, errNegativeNotAllowed
+ }
+ return uint16(s), nil
+ case bool:
+ if s {
+ return 1, nil
+ }
+ return 0, nil
+ case nil:
+ return 0, nil
+ default:
+ return 0, fmt.Errorf("unable to cast %#v of type %T to uint16", i, i)
+ }
+}
+
+// ToUint8E casts an interface to a uint type.
+func ToUint8E(i interface{}) (uint8, error) {
+ i = indirect(i)
+
+ intv, ok := toInt(i)
+ if ok {
+ if intv < 0 {
+ return 0, errNegativeNotAllowed
+ }
+ return uint8(intv), nil
+ }
+
+ switch s := i.(type) {
+ case string:
+ v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0)
+ if err == nil {
+ if v < 0 {
+ return 0, errNegativeNotAllowed
+ }
+ return uint8(v), nil
+ }
+ return 0, fmt.Errorf("unable to cast %#v of type %T to uint8", i, i)
+ case json.Number:
+ return ToUint8E(string(s))
+ case int64:
+ if s < 0 {
+ return 0, errNegativeNotAllowed
+ }
+ return uint8(s), nil
+ case int32:
+ if s < 0 {
+ return 0, errNegativeNotAllowed
+ }
+ return uint8(s), nil
+ case int16:
+ if s < 0 {
+ return 0, errNegativeNotAllowed
+ }
+ return uint8(s), nil
+ case int8:
+ if s < 0 {
+ return 0, errNegativeNotAllowed
+ }
+ return uint8(s), nil
+ case uint:
+ return uint8(s), nil
+ case uint64:
+ return uint8(s), nil
+ case uint32:
+ return uint8(s), nil
+ case uint16:
+ return uint8(s), nil
+ case uint8:
+ return s, nil
+ case float64:
+ if s < 0 {
+ return 0, errNegativeNotAllowed
+ }
+ return uint8(s), nil
+ case float32:
+ if s < 0 {
+ return 0, errNegativeNotAllowed
+ }
+ return uint8(s), nil
+ case bool:
+ if s {
+ return 1, nil
+ }
+ return 0, nil
+ case nil:
+ return 0, nil
+ default:
+ return 0, fmt.Errorf("unable to cast %#v of type %T to uint8", i, i)
+ }
+}
+
+// From html/template/content.go
+// Copyright 2011 The Go Authors. All rights reserved.
+// indirect returns the value, after dereferencing as many times
+// as necessary to reach the base type (or nil).
+func indirect(a interface{}) interface{} {
+ if a == nil {
+ return nil
+ }
+ if t := reflect.TypeOf(a); t.Kind() != reflect.Ptr {
+ // Avoid creating a reflect.Value if it's not a pointer.
+ return a
+ }
+ v := reflect.ValueOf(a)
+ for v.Kind() == reflect.Ptr && !v.IsNil() {
+ v = v.Elem()
+ }
+ return v.Interface()
+}
+
+// From html/template/content.go
+// Copyright 2011 The Go Authors. All rights reserved.
+// indirectToStringerOrError returns the value, after dereferencing as many times
+// as necessary to reach the base type (or nil) or an implementation of fmt.Stringer
+// or error,
+func indirectToStringerOrError(a interface{}) interface{} {
+ if a == nil {
+ return nil
+ }
+
+ var errorType = reflect.TypeOf((*error)(nil)).Elem()
+ var fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
+
+ v := reflect.ValueOf(a)
+ for !v.Type().Implements(fmtStringerType) && !v.Type().Implements(errorType) && v.Kind() == reflect.Ptr && !v.IsNil() {
+ v = v.Elem()
+ }
+ return v.Interface()
+}
+
+// ToStringE casts an interface to a string type.
+func ToStringE(i interface{}) (string, error) {
+ i = indirectToStringerOrError(i)
+
+ switch s := i.(type) {
+ case string:
+ return s, nil
+ case bool:
+ return strconv.FormatBool(s), nil
+ case float64:
+ return strconv.FormatFloat(s, 'f', -1, 64), nil
+ case float32:
+ return strconv.FormatFloat(float64(s), 'f', -1, 32), nil
+ case int:
+ return strconv.Itoa(s), nil
+ case int64:
+ return strconv.FormatInt(s, 10), nil
+ case int32:
+ return strconv.Itoa(int(s)), nil
+ case int16:
+ return strconv.FormatInt(int64(s), 10), nil
+ case int8:
+ return strconv.FormatInt(int64(s), 10), nil
+ case uint:
+ return strconv.FormatUint(uint64(s), 10), nil
+ case uint64:
+ return strconv.FormatUint(uint64(s), 10), nil
+ case uint32:
+ return strconv.FormatUint(uint64(s), 10), nil
+ case uint16:
+ return strconv.FormatUint(uint64(s), 10), nil
+ case uint8:
+ return strconv.FormatUint(uint64(s), 10), nil
+ case json.Number:
+ return s.String(), nil
+ case []byte:
+ return string(s), nil
+ case template.HTML:
+ return string(s), nil
+ case template.URL:
+ return string(s), nil
+ case template.JS:
+ return string(s), nil
+ case template.CSS:
+ return string(s), nil
+ case template.HTMLAttr:
+ return string(s), nil
+ case nil:
+ return "", nil
+ case fmt.Stringer:
+ return s.String(), nil
+ case error:
+ return s.Error(), nil
+ default:
+ return "", fmt.Errorf("unable to cast %#v of type %T to string", i, i)
+ }
+}
+
+// ToStringMapStringE casts an interface to a map[string]string type.
+func ToStringMapStringE(i interface{}) (map[string]string, error) {
+ var m = map[string]string{}
+
+ switch v := i.(type) {
+ case map[string]string:
+ return v, nil
+ case map[string]interface{}:
+ for k, val := range v {
+ m[ToString(k)] = ToString(val)
+ }
+ return m, nil
+ case map[interface{}]string:
+ for k, val := range v {
+ m[ToString(k)] = ToString(val)
+ }
+ return m, nil
+ case map[interface{}]interface{}:
+ for k, val := range v {
+ m[ToString(k)] = ToString(val)
+ }
+ return m, nil
+ case string:
+ err := jsonStringToObject(v, &m)
+ return m, err
+ default:
+ return m, fmt.Errorf("unable to cast %#v of type %T to map[string]string", i, i)
+ }
+}
+
+// ToStringMapStringSliceE casts an interface to a map[string][]string type.
+func ToStringMapStringSliceE(i interface{}) (map[string][]string, error) {
+ var m = map[string][]string{}
+
+ switch v := i.(type) {
+ case map[string][]string:
+ return v, nil
+ case map[string][]interface{}:
+ for k, val := range v {
+ m[ToString(k)] = ToStringSlice(val)
+ }
+ return m, nil
+ case map[string]string:
+ for k, val := range v {
+ m[ToString(k)] = []string{val}
+ }
+ case map[string]interface{}:
+ for k, val := range v {
+ switch vt := val.(type) {
+ case []interface{}:
+ m[ToString(k)] = ToStringSlice(vt)
+ case []string:
+ m[ToString(k)] = vt
+ default:
+ m[ToString(k)] = []string{ToString(val)}
+ }
+ }
+ return m, nil
+ case map[interface{}][]string:
+ for k, val := range v {
+ m[ToString(k)] = ToStringSlice(val)
+ }
+ return m, nil
+ case map[interface{}]string:
+ for k, val := range v {
+ m[ToString(k)] = ToStringSlice(val)
+ }
+ return m, nil
+ case map[interface{}][]interface{}:
+ for k, val := range v {
+ m[ToString(k)] = ToStringSlice(val)
+ }
+ return m, nil
+ case map[interface{}]interface{}:
+ for k, val := range v {
+ key, err := ToStringE(k)
+ if err != nil {
+ return m, fmt.Errorf("unable to cast %#v of type %T to map[string][]string", i, i)
+ }
+ value, err := ToStringSliceE(val)
+ if err != nil {
+ return m, fmt.Errorf("unable to cast %#v of type %T to map[string][]string", i, i)
+ }
+ m[key] = value
+ }
+ case string:
+ err := jsonStringToObject(v, &m)
+ return m, err
+ default:
+ return m, fmt.Errorf("unable to cast %#v of type %T to map[string][]string", i, i)
+ }
+ return m, nil
+}
+
+// ToStringMapBoolE casts an interface to a map[string]bool type.
+func ToStringMapBoolE(i interface{}) (map[string]bool, error) {
+ var m = map[string]bool{}
+
+ switch v := i.(type) {
+ case map[interface{}]interface{}:
+ for k, val := range v {
+ m[ToString(k)] = ToBool(val)
+ }
+ return m, nil
+ case map[string]interface{}:
+ for k, val := range v {
+ m[ToString(k)] = ToBool(val)
+ }
+ return m, nil
+ case map[string]bool:
+ return v, nil
+ case string:
+ err := jsonStringToObject(v, &m)
+ return m, err
+ default:
+ return m, fmt.Errorf("unable to cast %#v of type %T to map[string]bool", i, i)
+ }
+}
+
+// ToStringMapE casts an interface to a map[string]interface{} type.
+func ToStringMapE(i interface{}) (map[string]interface{}, error) {
+ var m = map[string]interface{}{}
+
+ switch v := i.(type) {
+ case map[interface{}]interface{}:
+ for k, val := range v {
+ m[ToString(k)] = val
+ }
+ return m, nil
+ case map[string]interface{}:
+ return v, nil
+ case string:
+ err := jsonStringToObject(v, &m)
+ return m, err
+ default:
+ return m, fmt.Errorf("unable to cast %#v of type %T to map[string]interface{}", i, i)
+ }
+}
+
+// ToStringMapIntE casts an interface to a map[string]int{} type.
+func ToStringMapIntE(i interface{}) (map[string]int, error) {
+ var m = map[string]int{}
+ if i == nil {
+ return m, fmt.Errorf("unable to cast %#v of type %T to map[string]int", i, i)
+ }
+
+ switch v := i.(type) {
+ case map[interface{}]interface{}:
+ for k, val := range v {
+ m[ToString(k)] = ToInt(val)
+ }
+ return m, nil
+ case map[string]interface{}:
+ for k, val := range v {
+ m[k] = ToInt(val)
+ }
+ return m, nil
+ case map[string]int:
+ return v, nil
+ case string:
+ err := jsonStringToObject(v, &m)
+ return m, err
+ }
+
+ if reflect.TypeOf(i).Kind() != reflect.Map {
+ return m, fmt.Errorf("unable to cast %#v of type %T to map[string]int", i, i)
+ }
+
+ mVal := reflect.ValueOf(m)
+ v := reflect.ValueOf(i)
+ for _, keyVal := range v.MapKeys() {
+ val, err := ToIntE(v.MapIndex(keyVal).Interface())
+ if err != nil {
+ return m, fmt.Errorf("unable to cast %#v of type %T to map[string]int", i, i)
+ }
+ mVal.SetMapIndex(keyVal, reflect.ValueOf(val))
+ }
+ return m, nil
+}
+
+// ToStringMapInt64E casts an interface to a map[string]int64{} type.
+func ToStringMapInt64E(i interface{}) (map[string]int64, error) {
+ var m = map[string]int64{}
+ if i == nil {
+ return m, fmt.Errorf("unable to cast %#v of type %T to map[string]int64", i, i)
+ }
+
+ switch v := i.(type) {
+ case map[interface{}]interface{}:
+ for k, val := range v {
+ m[ToString(k)] = ToInt64(val)
+ }
+ return m, nil
+ case map[string]interface{}:
+ for k, val := range v {
+ m[k] = ToInt64(val)
+ }
+ return m, nil
+ case map[string]int64:
+ return v, nil
+ case string:
+ err := jsonStringToObject(v, &m)
+ return m, err
+ }
+
+ if reflect.TypeOf(i).Kind() != reflect.Map {
+ return m, fmt.Errorf("unable to cast %#v of type %T to map[string]int64", i, i)
+ }
+ mVal := reflect.ValueOf(m)
+ v := reflect.ValueOf(i)
+ for _, keyVal := range v.MapKeys() {
+ val, err := ToInt64E(v.MapIndex(keyVal).Interface())
+ if err != nil {
+ return m, fmt.Errorf("unable to cast %#v of type %T to map[string]int64", i, i)
+ }
+ mVal.SetMapIndex(keyVal, reflect.ValueOf(val))
+ }
+ return m, nil
+}
+
+// ToSliceE casts an interface to a []interface{} type.
+func ToSliceE(i interface{}) ([]interface{}, error) {
+ var s []interface{}
+
+ switch v := i.(type) {
+ case []interface{}:
+ return append(s, v...), nil
+ case []map[string]interface{}:
+ for _, u := range v {
+ s = append(s, u)
+ }
+ return s, nil
+ default:
+ return s, fmt.Errorf("unable to cast %#v of type %T to []interface{}", i, i)
+ }
+}
+
+// ToBoolSliceE casts an interface to a []bool type.
+func ToBoolSliceE(i interface{}) ([]bool, error) {
+ if i == nil {
+ return []bool{}, fmt.Errorf("unable to cast %#v of type %T to []bool", i, i)
+ }
+
+ switch v := i.(type) {
+ case []bool:
+ return v, nil
+ }
+
+ kind := reflect.TypeOf(i).Kind()
+ switch kind {
+ case reflect.Slice, reflect.Array:
+ s := reflect.ValueOf(i)
+ a := make([]bool, s.Len())
+ for j := 0; j < s.Len(); j++ {
+ val, err := ToBoolE(s.Index(j).Interface())
+ if err != nil {
+ return []bool{}, fmt.Errorf("unable to cast %#v of type %T to []bool", i, i)
+ }
+ a[j] = val
+ }
+ return a, nil
+ default:
+ return []bool{}, fmt.Errorf("unable to cast %#v of type %T to []bool", i, i)
+ }
+}
+
+// ToStringSliceE casts an interface to a []string type.
+func ToStringSliceE(i interface{}) ([]string, error) {
+ var a []string
+
+ switch v := i.(type) {
+ case []interface{}:
+ for _, u := range v {
+ a = append(a, ToString(u))
+ }
+ return a, nil
+ case []string:
+ return v, nil
+ case []int8:
+ for _, u := range v {
+ a = append(a, ToString(u))
+ }
+ return a, nil
+ case []int:
+ for _, u := range v {
+ a = append(a, ToString(u))
+ }
+ return a, nil
+ case []int32:
+ for _, u := range v {
+ a = append(a, ToString(u))
+ }
+ return a, nil
+ case []int64:
+ for _, u := range v {
+ a = append(a, ToString(u))
+ }
+ return a, nil
+ case []float32:
+ for _, u := range v {
+ a = append(a, ToString(u))
+ }
+ return a, nil
+ case []float64:
+ for _, u := range v {
+ a = append(a, ToString(u))
+ }
+ return a, nil
+ case string:
+ return strings.Fields(v), nil
+ case []error:
+ for _, err := range i.([]error) {
+ a = append(a, err.Error())
+ }
+ return a, nil
+ case interface{}:
+ str, err := ToStringE(v)
+ if err != nil {
+ return a, fmt.Errorf("unable to cast %#v of type %T to []string", i, i)
+ }
+ return []string{str}, nil
+ default:
+ return a, fmt.Errorf("unable to cast %#v of type %T to []string", i, i)
+ }
+}
+
+// ToIntSliceE casts an interface to a []int type.
+func ToIntSliceE(i interface{}) ([]int, error) {
+ if i == nil {
+ return []int{}, fmt.Errorf("unable to cast %#v of type %T to []int", i, i)
+ }
+
+ switch v := i.(type) {
+ case []int:
+ return v, nil
+ }
+
+ kind := reflect.TypeOf(i).Kind()
+ switch kind {
+ case reflect.Slice, reflect.Array:
+ s := reflect.ValueOf(i)
+ a := make([]int, s.Len())
+ for j := 0; j < s.Len(); j++ {
+ val, err := ToIntE(s.Index(j).Interface())
+ if err != nil {
+ return []int{}, fmt.Errorf("unable to cast %#v of type %T to []int", i, i)
+ }
+ a[j] = val
+ }
+ return a, nil
+ default:
+ return []int{}, fmt.Errorf("unable to cast %#v of type %T to []int", i, i)
+ }
+}
+
+// ToDurationSliceE casts an interface to a []time.Duration type.
+func ToDurationSliceE(i interface{}) ([]time.Duration, error) {
+ if i == nil {
+ return []time.Duration{}, fmt.Errorf("unable to cast %#v of type %T to []time.Duration", i, i)
+ }
+
+ switch v := i.(type) {
+ case []time.Duration:
+ return v, nil
+ }
+
+ kind := reflect.TypeOf(i).Kind()
+ switch kind {
+ case reflect.Slice, reflect.Array:
+ s := reflect.ValueOf(i)
+ a := make([]time.Duration, s.Len())
+ for j := 0; j < s.Len(); j++ {
+ val, err := ToDurationE(s.Index(j).Interface())
+ if err != nil {
+ return []time.Duration{}, fmt.Errorf("unable to cast %#v of type %T to []time.Duration", i, i)
+ }
+ a[j] = val
+ }
+ return a, nil
+ default:
+ return []time.Duration{}, fmt.Errorf("unable to cast %#v of type %T to []time.Duration", i, i)
+ }
+}
+
+// StringToDate attempts to parse a string into a time.Time type using a
+// predefined list of formats. If no suitable format is found, an error is
+// returned.
+func StringToDate(s string) (time.Time, error) {
+ return parseDateWith(s, time.UTC, timeFormats)
+}
+
+// StringToDateInDefaultLocation casts an empty interface to a time.Time,
+// interpreting inputs without a timezone to be in the given location,
+// or the local timezone if nil.
+func StringToDateInDefaultLocation(s string, location *time.Location) (time.Time, error) {
+ return parseDateWith(s, location, timeFormats)
+}
+
+type timeFormatType int
+
+const (
+ timeFormatNoTimezone timeFormatType = iota
+ timeFormatNamedTimezone
+ timeFormatNumericTimezone
+ timeFormatNumericAndNamedTimezone
+ timeFormatTimeOnly
+)
+
+type timeFormat struct {
+ format string
+ typ timeFormatType
+}
+
+func (f timeFormat) hasTimezone() bool {
+ // We don't include the formats with only named timezones, see
+ // https://github.com/golang/go/issues/19694#issuecomment-289103522
+ return f.typ >= timeFormatNumericTimezone && f.typ <= timeFormatNumericAndNamedTimezone
+}
+
+var (
+ timeFormats = []timeFormat{
+ // Keep common formats at the top.
+ {"2006-01-02", timeFormatNoTimezone},
+ {time.RFC3339, timeFormatNumericTimezone},
+ {"2006-01-02T15:04:05", timeFormatNoTimezone}, // iso8601 without timezone
+ {time.RFC1123Z, timeFormatNumericTimezone},
+ {time.RFC1123, timeFormatNamedTimezone},
+ {time.RFC822Z, timeFormatNumericTimezone},
+ {time.RFC822, timeFormatNamedTimezone},
+ {time.RFC850, timeFormatNamedTimezone},
+ {"2006-01-02 15:04:05.999999999 -0700 MST", timeFormatNumericAndNamedTimezone}, // Time.String()
+ {"2006-01-02T15:04:05-0700", timeFormatNumericTimezone}, // RFC3339 without timezone hh:mm colon
+ {"2006-01-02 15:04:05Z0700", timeFormatNumericTimezone}, // RFC3339 without T or timezone hh:mm colon
+ {"2006-01-02 15:04:05", timeFormatNoTimezone},
+ {time.ANSIC, timeFormatNoTimezone},
+ {time.UnixDate, timeFormatNamedTimezone},
+ {time.RubyDate, timeFormatNumericTimezone},
+ {"2006-01-02 15:04:05Z07:00", timeFormatNumericTimezone},
+ {"02 Jan 2006", timeFormatNoTimezone},
+ {"2006-01-02 15:04:05 -07:00", timeFormatNumericTimezone},
+ {"2006-01-02 15:04:05 -0700", timeFormatNumericTimezone},
+ {time.Kitchen, timeFormatTimeOnly},
+ {time.Stamp, timeFormatTimeOnly},
+ {time.StampMilli, timeFormatTimeOnly},
+ {time.StampMicro, timeFormatTimeOnly},
+ {time.StampNano, timeFormatTimeOnly},
+ }
+)
+
+func parseDateWith(s string, location *time.Location, formats []timeFormat) (d time.Time, e error) {
+
+ for _, format := range formats {
+ if d, e = time.Parse(format.format, s); e == nil {
+
+ // Some time formats have a zone name, but no offset, so it gets
+ // put in that zone name (not the default one passed in to us), but
+ // without that zone's offset. So set the location manually.
+ if format.typ <= timeFormatNamedTimezone {
+ if location == nil {
+ location = time.Local
+ }
+ year, month, day := d.Date()
+ hour, min, sec := d.Clock()
+ d = time.Date(year, month, day, hour, min, sec, d.Nanosecond(), location)
+ }
+
+ return
+ }
+ }
+ return d, fmt.Errorf("unable to parse date: %s", s)
+}
+
+// jsonStringToObject attempts to unmarshall a string as JSON into
+// the object passed as pointer.
+func jsonStringToObject(s string, v interface{}) error {
+ data := []byte(s)
+ return json.Unmarshal(data, v)
+}
+
+// toInt returns the int value of v if v or v's underlying type
+// is an int.
+// Note that this will return false for int64 etc. types.
+func toInt(v interface{}) (int, bool) {
+ switch v := v.(type) {
+ case int:
+ return v, true
+ case time.Weekday:
+ return int(v), true
+ case time.Month:
+ return int(v), true
+ default:
+ return 0, false
+ }
+}
+
+func trimZeroDecimal(s string) string {
+ var foundZero bool
+ for i := len(s); i > 0; i-- {
+ switch s[i-1] {
+ case '.':
+ if foundZero {
+ return s[:i-1]
+ }
+ case '0':
+ foundZero = true
+ default:
+ return s
+ }
+ }
+ return s
+}
diff --git a/vendor/github.com/spf13/cast/timeformattype_string.go b/vendor/github.com/spf13/cast/timeformattype_string.go
new file mode 100644
index 00000000..1524fc82
--- /dev/null
+++ b/vendor/github.com/spf13/cast/timeformattype_string.go
@@ -0,0 +1,27 @@
+// Code generated by "stringer -type timeFormatType"; DO NOT EDIT.
+
+package cast
+
+import "strconv"
+
+func _() {
+ // An "invalid array index" compiler error signifies that the constant values have changed.
+ // Re-run the stringer command to generate them again.
+ var x [1]struct{}
+ _ = x[timeFormatNoTimezone-0]
+ _ = x[timeFormatNamedTimezone-1]
+ _ = x[timeFormatNumericTimezone-2]
+ _ = x[timeFormatNumericAndNamedTimezone-3]
+ _ = x[timeFormatTimeOnly-4]
+}
+
+const _timeFormatType_name = "timeFormatNoTimezonetimeFormatNamedTimezonetimeFormatNumericTimezonetimeFormatNumericAndNamedTimezonetimeFormatTimeOnly"
+
+var _timeFormatType_index = [...]uint8{0, 20, 43, 68, 101, 119}
+
+func (i timeFormatType) String() string {
+ if i < 0 || i >= timeFormatType(len(_timeFormatType_index)-1) {
+ return "timeFormatType(" + strconv.FormatInt(int64(i), 10) + ")"
+ }
+ return _timeFormatType_name[_timeFormatType_index[i]:_timeFormatType_index[i+1]]
+}
diff --git a/vendor/github.com/spf13/cobra/.gitignore b/vendor/github.com/spf13/cobra/.gitignore
new file mode 100644
index 00000000..c7b459e4
--- /dev/null
+++ b/vendor/github.com/spf13/cobra/.gitignore
@@ -0,0 +1,39 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+# Vim files https://github.com/github/gitignore/blob/master/Global/Vim.gitignore
+# swap
+[._]*.s[a-w][a-z]
+[._]s[a-w][a-z]
+# session
+Session.vim
+# temporary
+.netrwhist
+*~
+# auto-generated tag files
+tags
+
+*.exe
+cobra.test
+bin
+
+.idea/
+*.iml
diff --git a/vendor/github.com/spf13/cobra/.golangci.yml b/vendor/github.com/spf13/cobra/.golangci.yml
new file mode 100644
index 00000000..2c8f4808
--- /dev/null
+++ b/vendor/github.com/spf13/cobra/.golangci.yml
@@ -0,0 +1,57 @@
+# Copyright 2013-2023 The Cobra Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+run:
+ deadline: 5m
+
+linters:
+ disable-all: true
+ enable:
+ #- bodyclose
+ # - deadcode ! deprecated since v1.49.0; replaced by 'unused'
+ #- depguard
+ #- dogsled
+ #- dupl
+ - errcheck
+ #- exhaustive
+ #- funlen
+ #- gochecknoinits
+ - goconst
+ - gocritic
+ #- gocyclo
+ - gofmt
+ - goimports
+ #- gomnd
+ #- goprintffuncname
+ - gosec
+ - gosimple
+ - govet
+ - ineffassign
+ #- lll
+ - misspell
+ #- nakedret
+ #- noctx
+ - nolintlint
+ #- rowserrcheck
+ #- scopelint
+ - staticcheck
+ #- structcheck ! deprecated since v1.49.0; replaced by 'unused'
+ - stylecheck
+ #- typecheck
+ - unconvert
+ #- unparam
+ - unused
+ # - varcheck ! deprecated since v1.49.0; replaced by 'unused'
+ #- whitespace
+ fast: false
diff --git a/vendor/github.com/spf13/cobra/.mailmap b/vendor/github.com/spf13/cobra/.mailmap
new file mode 100644
index 00000000..94ec5306
--- /dev/null
+++ b/vendor/github.com/spf13/cobra/.mailmap
@@ -0,0 +1,3 @@
+Steve Francia