diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 58d3eb9..4f0a591 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -5,8 +5,6 @@ on: branches: [ main ] pull_request: branches: [ main ] - schedule: - - cron: '27 05 * * 6' jobs: analyze: diff --git a/README.md b/README.md index e96b54c..38041a8 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,5 @@ -External Docs -------------- - -### Kong ### - -* [Kong](https://github.com/alecthomas/kong) - -### Docopt ### - -* [Main Project](http://docopt.org/) -* [Docopt-go](https://godoc.org/github.com/docopt/docopt-go) - => Bug: Leerzeilen in Options-Doc Fork? Oder Kong? +Notes & External Docs +--------------------- ### Cross Compiling ### diff --git a/chdiff/chdiff.go b/chdiff/chdiff.go index 00ceba0..bd24a5a 100644 --- a/chdiff/chdiff.go +++ b/chdiff/chdiff.go @@ -2,9 +2,9 @@ package chdiff import ( _ "embed" - "fmt" "log" "os" + "path" "github.com/alecthomas/kong" "github.com/soerenkoehler/chdiff-go/digest" @@ -20,8 +20,8 @@ var cli struct { } type cmdDigest struct { - Path string `arg:"" name:"PATH" type:"path" default:"." help:"Path for which to calculate the digest"` - Mode string `name:"mode" short:"m" help:"The checksum algorithm to use [SHA256,SHA512]." enum:"SHA256,SHA512" default:"SHA256"` + Path string `arg:"" name:"PATH" type:"path" default:"." help:"Path for which to calculate the digest"` + Algorithm string `name:"alg" help:"The checksum algorithm to use [SHA256,SHA512]." enum:"SHA256,SHA512" default:"SHA256"` } func DoMain( @@ -30,8 +30,6 @@ func DoMain( digestService digest.Service, stdioService util.StdIOService) { - var err error - os.Args = args log.SetOutput(stdioService.Stdout()) @@ -47,16 +45,18 @@ func DoMain( switch ctx.Command() { case "create", "create ": - err = digestService.Create(cli.Create.Path, "out.txt", cli.Create.Mode) + digestService.Create( + cli.Create.Path, + path.Join(cli.Create.Path, "out.txt"), + cli.Create.Algorithm) case "verify", "verify ": - err = digestService.Verify(cli.Verify.Path, "out.txt", cli.Verify.Mode) + digestService.Verify( + cli.Verify.Path, + path.Join(cli.Verify.Path, "out.txt"), + cli.Verify.Algorithm) default: - err = fmt.Errorf("unknown command: %s", ctx.Command()) - } - - if err != nil { - log.Fatalf("Error: %s", err) + log.Fatalf("unknown command: %s", ctx.Command()) } } diff --git a/chdiff/chdiff_test.go b/chdiff/chdiff_test.go index f0d3f48..59882bc 100644 --- a/chdiff/chdiff_test.go +++ b/chdiff/chdiff_test.go @@ -1,10 +1,10 @@ -package chdiff_test +package chdiff import ( + "path" "path/filepath" "testing" - "github.com/soerenkoehler/chdiff-go/chdiff" "github.com/soerenkoehler/chdiff-go/util" "github.com/soerenkoehler/go-testutils/mockutil" ) @@ -13,34 +13,35 @@ type digestServiceMock struct { mockutil.Registry } -func (mock digestServiceMock) Create(dataPath, digestPath, mode string) error { +func (mock digestServiceMock) Create(dataPath, digestPath, algorithm string) { mockutil.Register( &mock.Registry, - mockutil.Call{"create", dataPath, digestPath, mode}) - return nil + mockutil.Call{"create", dataPath, digestPath, algorithm}) } -func (mock *digestServiceMock) Verify(dataPath, digestPath, mode string) error { +func (mock *digestServiceMock) Verify(dataPath, digestPath, algorithm string) { mockutil.Register( &mock.Registry, - mockutil.Call{"verify", dataPath, digestPath, mode}) - return nil + mockutil.Call{"verify", dataPath, digestPath, algorithm}) } func expectDigestServiceCall( t *testing.T, args []string, - call, dataPath, digestPath, mode string) { + call, dataPath, digestPath, algorithm string) { absDataPath, _ := filepath.Abs(dataPath) + absDigestPath := path.Join(absDataPath, digestPath) - digestService := &digestServiceMock{} + digestService := &digestServiceMock{ + Registry: mockutil.Registry{T: t}, + } - chdiff.DoMain("TEST", args, digestService, util.DefaultStdIOService{}) + DoMain("TEST", args, digestService, util.DefaultStdIOService{}) - mockutil.Verify(t, + mockutil.Verify( &digestService.Registry, - mockutil.Call{call, absDataPath, digestPath, mode}) + mockutil.Call{call, absDataPath, absDigestPath, algorithm}) } func TestCmdVerifyIsDefault(t *testing.T) { diff --git a/digest/digest.go b/digest/digest.go index 586b599..3a68952 100644 --- a/digest/digest.go +++ b/digest/digest.go @@ -1,86 +1,156 @@ package digest import ( + "crypto/sha256" + "crypto/sha512" + "encoding/hex" "fmt" + "hash" + "io" "log" "os" "path" + "path/filepath" "sort" "sync" + "time" "github.com/soerenkoehler/chdiff-go/util" ) +type DigestEntry struct { + file string + hash string + size int64 + modTime time.Time +} + +type Digest map[string]DigestEntry + +type DigestContext struct { + rootpath string + algorithm string + waitgroup *sync.WaitGroup + digest chan DigestEntry +} + // Service is the mockable API for the digest service. type Service interface { - Create(dataPath, digestPath, mode string) error - Verify(dataPath, digestPath, mode string) error + Create(dataPath, digestPath, algorithm string) + Verify(dataPath, digestPath, algorithm string) } // DefaultService ist the production implementation of the digest service. type DefaultService struct{} -// Digest is a map file path => checksum -type Digest map[string]string - -// Create ... TODO -func (DefaultService) Create(dataPath, digestPath, mode string) error { - digest, err := calculate(dataPath, mode) +func (DefaultService) Create(dataPath, digestPath, algorithm string) { + digest := calculateDigest(dataPath, algorithm) fmt.Printf("Saving %s\n", digestPath) for _, k := range digest.sortedKeys() { - fmt.Printf("%s => %s\n", k, digest[k]) + fmt.Print(digest[k].entryToString()) } - return err } -// Verify ... TODO -func (DefaultService) Verify(dataPath, digestPath, mode string) error { - digest, err := calculate(dataPath, mode) +func (DefaultService) Verify(dataPath, digestPath, algorithm string) { + digest := calculateDigest(dataPath, algorithm) fmt.Printf("Verify %s\n", digestPath) for _, k := range digest.sortedKeys() { - fmt.Printf("%s => %s\n", k, digest[k]) + fmt.Print(digest[k].entryToString()) } - return err } -func calculate(rootPath, mode string) (Digest, error) { - wait := sync.WaitGroup{} - - var processPath, processDir, processFile func(string) - - processPath = func(path string) { - wait.Add(1) - go func() { - switch info := util.Stat(path); { - case info.IsSymlink: - log.Printf("skipping symlink: %s => %s", path, info.Target) - case info.IsDir: - processDir(path) - default: - processFile(path) - } - wait.Done() - }() +func calculateDigest(rootpath, algorithm string) Digest { + context := DigestContext{ + rootpath: rootpath, + algorithm: algorithm, + waitgroup: &sync.WaitGroup{}, + digest: make(chan DigestEntry), } - processDir = func(dirPath string) { - entries, err := os.ReadDir(dirPath) - if err != nil { - log.Println(err) + go func() { + context.processPath(context.rootpath) + context.waitgroup.Wait() + close(context.digest) + }() + + result := Digest{} + for entry := range context.digest { + result[entry.file] = entry + } + + return result +} + +func (context DigestContext) processPath(path string) { + context.waitgroup.Add(1) + go func() { + switch info := util.Stat(path); { + case info.IsSymlink: + log.Printf("[W] skipping symlink: %s => %s", path, info.Target) + case info.IsDir: + context.processDir(path) + default: + context.processFile(path) } + context.waitgroup.Done() + }() +} + +func (context DigestContext) processDir(dir string) { + entries, err := os.ReadDir(dir) + if err != nil { + log.Printf("[E]: %s\n", err) + } else { for _, entry := range entries { - processPath(path.Join(dirPath, entry.Name())) + context.processPath(path.Join(dir, entry.Name())) } } +} + +func (context DigestContext) processFile(file string) { + info, err := os.Lstat(file) + if err != nil { + log.Printf("[E]: %s\n", err) + return + } + + relativePath, err := filepath.Rel(context.rootpath, file) + if err != nil { + log.Printf("[E]: %s\n", err) + return + } + + checksum, err := context.getNewHash() + if err != nil { + log.Printf("[E]: %s\n", err) + return + } - processFile = func(path string) { - // fmt.Printf("calculate(%s, %s)\n", mode, path) + input, err := os.Open(file) + if err != nil { + log.Printf("[E]: %s\n", err) + return } - processPath(rootPath) - wait.Wait() + defer input.Close() + io.Copy(checksum, input) - return Digest{}, nil + context.digest <- DigestEntry{ + file: relativePath, + hash: hex.EncodeToString(checksum.Sum(nil)), + size: info.Size(), + modTime: info.ModTime(), + } +} + +func (context DigestContext) getNewHash() (hash.Hash, error) { + switch context.algorithm { + case "SHA256": + return sha256.New(), nil + case "SHA512": + return sha512.New(), nil + } + return nil, fmt.Errorf("invalid hash algorithm %v", context.algorithm) } func (digest Digest) sortedKeys() []string { @@ -91,3 +161,13 @@ func (digest Digest) sortedKeys() []string { sort.Strings(keys) return keys } + +func (entry DigestEntry) entryToString() string { + return fmt.Sprintf( + "# %d %s %s\n%s *%s\n", + entry.size, + entry.modTime.Local().Format("20060102-150405"), + entry.file, + entry.hash, + entry.file) +} diff --git a/digest/digest_test.go b/digest/digest_test.go new file mode 100644 index 0000000..cb55733 --- /dev/null +++ b/digest/digest_test.go @@ -0,0 +1,118 @@ +package digest + +import ( + "path" + "testing" + + "github.com/soerenkoehler/go-testutils/datautil" + // "github.com/google/go-cmp/cmp" +) + +type testCase struct { + path string + size int64 + seed uint64 + hash string +} + +func TestWrongAlgorithm(t *testing.T) { + if len(createDigest(t, []testCase{{ + path: "invalid", + size: 0, + seed: 1, + hash: "invalid", + }}, "INVALID")) != 0 { + t.Fatal("invalid algorithm must not create digest entries") + } +} + +func TestDigest256(t *testing.T) { + runDigestCalculationTest(t, []testCase{{ + path: "zero", + size: 0, + seed: 1, + hash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + }, { + path: "data_1", + size: 256, + seed: 1, + hash: "a6452fbd8c12f8df622c1ca4c567f966801fb56442aca03b4e1303e7a412a9d5", + }, { + path: "sub/data_1", + size: 256, + seed: 1, + hash: "a6452fbd8c12f8df622c1ca4c567f966801fb56442aca03b4e1303e7a412a9d5", + }}, "SHA256") +} + +func TestDigest512(t *testing.T) { + runDigestCalculationTest(t, []testCase{{ + path: "zero", + size: 0, + seed: 1, + hash: "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e", + }, { + path: "data_1", + size: 256, + seed: 1, + hash: "f3f00e46e5dc3819b8268afedb1221f25a4c29d3223979ede1df107155cc75bd427a5795b820fbd83fd4785899cb9de201b770a2c88a3bed90be37e82156e10b", + }, { + path: "sub/data_1", + size: 256, + seed: 1, + hash: "f3f00e46e5dc3819b8268afedb1221f25a4c29d3223979ede1df107155cc75bd427a5795b820fbd83fd4785899cb9de201b770a2c88a3bed90be37e82156e10b", + }}, "SHA512") +} + +func runDigestCalculationTest( + t *testing.T, + data []testCase, + algorithm string) { + + verifyDigest(t, data, + createDigest(t, data, algorithm)) +} + +func createDigest( + t *testing.T, + data []testCase, + algorithm string) Digest { + + root := t.TempDir() + + for _, dataPoint := range data { + file := path.Join(root, dataPoint.path) + datautil.CreateRandomFile(file, dataPoint.size, dataPoint.seed) + } + + return calculateDigest(root, algorithm) +} + +func verifyDigest( + t *testing.T, + data []testCase, + digest Digest) { + + if len(digest) != len(data) { + t.Fatal("Digest size must match number of input data points") + } + + for _, dataPoint := range data { + expectedPath := dataPoint.path + actualPath := digest[expectedPath].file + if actualPath != expectedPath { + t.Errorf("DigestEntry.file (%v) must match Digest map key (%v)", + actualPath, + expectedPath) + } + + expectedHash := dataPoint.hash + actualHash := digest[expectedPath].hash + if actualHash != expectedHash { + t.Errorf("actual hash (%v) does not match expected hash (%v) (test file: %v)", + actualHash, + expectedHash, + expectedPath) + } + } +} diff --git a/go.mod b/go.mod index f9932da..2aa283b 100644 --- a/go.mod +++ b/go.mod @@ -4,5 +4,5 @@ go 1.16 require ( github.com/alecthomas/kong v0.2.16 - github.com/soerenkoehler/go-testutils v0.0.0-20210326222158-2e995060044f + github.com/soerenkoehler/go-testutils v0.0.1 ) diff --git a/go.sum b/go.sum index de92426..3bb9170 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,13 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= dmitri.shuralyov.com/gpu/mtl v0.0.0-20201218220906-28db891af037/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/alecthomas/kong v0.2.16 h1:F232CiYSn54Tnl1sJGTeHmx4vJDNLVP2b9yCVMOQwHQ= github.com/alecthomas/kong v0.2.16/go.mod h1:kQOmtJgV+Lb4aj+I2LEn40cbtawdWJ9Y8QLq+lElKxE= github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -19,8 +21,14 @@ github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -29,14 +37,29 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/rs/zerolog v1.21.0/go.mod h1:ZPhntP/xmq1nnND05hhpAh2QMhSsA4UN3MGZ6O2J3hM= github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= -github.com/soerenkoehler/go-testutils v0.0.0-20210326222158-2e995060044f h1:+RxYXP7gBYbOTVPp0qxwjQVhVIRA/nvvN5rP+9Kzhq8= -github.com/soerenkoehler/go-testutils v0.0.0-20210326222158-2e995060044f/go.mod h1:4PM3uhtpBLhe09kmL7TjudEXnz7PIAbD2lVAonTKMDo= -github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/soerenkoehler/go-testutils v0.0.1 h1:UtjfiYjsVBbSkaSiCIZxzBr8ELOPZIFnL8EKG6P0gwk= +github.com/soerenkoehler/go-testutils v0.0.1/go.mod h1:t3RxsVNLxAxEtG+bZw79DyfPBmDnkwB+8XTwc+PUjRk= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -44,7 +67,8 @@ golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= -golang.org/x/exp v0.0.0-20210220032938-85be41e4509f/go.mod h1:I6l2HNBLBZEcrOoCpyKLdY2lHoRZ8lI4x60KMCQDft4= +golang.org/x/exp v0.0.0-20210514180818-737f94c0881e h1:VqVU3dsTLGDa5pW74b+xG1lvKltt4EZIUrFPeKOqV2s= +golang.org/x/exp v0.0.0-20210514180818-737f94c0881e/go.mod h1:MSdmUWF4ZWBPSUbgUX/gaau5kvnbkSs9pgtY6B9JXDE= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -54,42 +78,64 @@ golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+o golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mobile v0.0.0-20201217150744-e6ae53a27f4f/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.8.1-0.20200930085651-eea0b5cb5cc9/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= -gonum.org/v1/gonum v0.9.0/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0= +gonum.org/v1/gonum v0.9.1 h1:HCWmqqNoELL0RAQeKBXWtkp04mGk8koafcB4He6+uhc= +gonum.org/v1/gonum v0.9.1/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/netlib v0.0.0-20210302091547-ede94419cf37/go.mod h1:zQa7n16lh3Z6FbSTYgjG+KNhz1bA/b9t3plFEaGMp+A= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=