diff --git a/cmd/cogger/main.go b/cmd/cogger/main.go index 3ec2741..c82e1e5 100644 --- a/cmd/cogger/main.go +++ b/cmd/cogger/main.go @@ -2,63 +2,132 @@ package main import ( "context" - "flag" "fmt" + "io" "os" - "path/filepath" + "os/signal" + "strconv" + "strings" + "syscall" "github.com/airbusgeo/cogger" + "github.com/spf13/cobra" "github.com/google/tiff" _ "github.com/google/tiff/bigtiff" ) func main() { - ctx := context.Background() - err := run(ctx) - if err != nil { - fmt.Fprintf(os.Stderr, err.Error()) - os.Exit(1) + ctx, cncl := signal.NotifyContext(context.Background(), + syscall.SIGINT, syscall.SIGTERM) + defer cncl() + exitCode := 0 + if err := newRootCommand().ExecuteContext(ctx); err != nil { + exitCode = 1 } + os.Exit(exitCode) } -func run(ctx context.Context) error { - outfile := flag.String("output", "out.tif", "destination file") - flag.Parse() - - args := flag.Args() - if len(args) < 1 { - fmt.Fprintf(flag.CommandLine.Output(), "Usage: %s [options] file.tif [overview.tif...]\nOptions:\n", filepath.Base(os.Args[0])) - flag.PrintDefaults() - return fmt.Errorf("") +func newRootCommand() *cobra.Command { + outfile := "out.tif" + skipGhostAreas := false + keepBigtiff := false + forceBigtiff := false + keptOverviewsS := "" + keptMasksS := "" + var keptMasks []int = nil + var keptOverviews []int = nil + cmd := &cobra.Command{ + Use: "cogger [main.tif] [overview.tif]...", + Short: "cogger is a tool for creating Cloud Optimized GeoTIFFs", + Args: cobra.MinimumNArgs(1), + PreRunE: func(cmd *cobra.Command, _ []string) error { + flags := cmd.Flags() + if flags.Changed("keep-masks") { + if keptMasksS == "" { + keptMasks = []int{} + } else { + ks := strings.Split(keptMasksS, ",") + keptMasks = make([]int, len(ks)) + for k := range ks { + kv, err := strconv.Atoi(ks[k]) + if err != nil { + return fmt.Errorf("invalid mask index %s: %w", ks[k], err) + } + keptMasks[k] = kv + } + } + } + if flags.Changed("keep-overviews") { + if keptOverviewsS == "" { + keptOverviews = []int{} + } else { + ks := strings.Split(keptOverviewsS, ",") + keptOverviews = make([]int, len(ks)) + for k := range ks { + kv, err := strconv.Atoi(ks[k]) + if err != nil { + return fmt.Errorf("invalid overview index %s: %w", ks[k], err) + } + keptOverviews[k] = kv + } + } + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + readers := make([]tiff.ReadAtReadSeeker, len(args)) + for i, input := range args { + topFile, err := os.Open(input) + if err != nil { + return fmt.Errorf("open %s: %w", args[0], err) + } + defer topFile.Close() + readers[i] = topFile + } + out, err := os.Create(outfile) + if err != nil { + return fmt.Errorf("create %s: %w", outfile, err) + } + cfg := cogger.DefaultConfig() + if keepBigtiff { + tif0, err := tiff.Parse(readers[0], nil, nil) + if err != nil { + return fmt.Errorf("parse %s: %w", args[0], err) + } + if tif0.Version() == 0x2B { + cfg.BigTIFF = true + } + readers[0].Seek(0, io.SeekStart) + } + if forceBigtiff { + cfg.BigTIFF = true + } + if skipGhostAreas { + cfg.WithGDALGhostArea = false + } + cfg.KeptMasks = keptMasks + cfg.KeptOverviews = keptOverviews + err = cfg.Rewrite(out, readers...) + if err != nil { + return fmt.Errorf("cogger.rewrite: %w", err) + } + err = out.Close() + if err != nil { + return fmt.Errorf("close %s: %w", outfile, err) + } + return nil + }, } + flags := cmd.Flags() + flags.StringVar(&outfile, "output", outfile, "destination file") + flags.BoolVar(&skipGhostAreas, "skip-gdal-ghost-areas", skipGhostAreas, "omit writing gdal ghost areas") + flags.BoolVar(&keepBigtiff, "keep-bigtiff", keepBigtiff, "produce a bigtiff file if the input is bigtiff") + flags.BoolVar(&forceBigtiff, "force-bigtiff", forceBigtiff, "produce a bigtiff output even if the size is less than 4Gb") + flags.StringVar(&keptOverviewsS, "keep-overviews", "", "comma separated list of overview levels to keep") + flags.StringVar(&keptMasksS, "keep-masks", "", "comma separated list of mask levels to keep") + cmd.MarkFlagsMutuallyExclusive("keep-bigtiff", "force-bigtiff") + flags.SortFlags = false - totalSize := int64(0) - readers := make([]tiff.ReadAtReadSeeker, len(args)) - for i, input := range args { - topFile, err := os.Open(input) - if err != nil { - return fmt.Errorf("open %s: %w", args[0], err) - } - defer topFile.Close() - st, err := topFile.Stat() - if err != nil { - return fmt.Errorf("stat %s: %w", args[0], err) - } - totalSize += st.Size() - readers[i] = topFile - } - out, err := os.Create(*outfile) - if err != nil { - return fmt.Errorf("create %s: %w", *outfile, err) - } - err = cogger.Rewrite(out, readers...) - if err != nil { - return fmt.Errorf("mucog write: %w", err) - } - err = out.Close() - if err != nil { - return fmt.Errorf("close %s: %w", *outfile, err) - } - return nil + return cmd } diff --git a/cmd/tiler/Dockerfile b/cmd/tiler/Dockerfile new file mode 100644 index 0000000..9d4236c --- /dev/null +++ b/cmd/tiler/Dockerfile @@ -0,0 +1,94 @@ +FROM golang:bullseye AS builder +ARG COGGER_IMAGE + +RUN apt update && apt -y install \ + build-essential \ + cmake \ + curl \ + libbrotli-dev \ + libgflags-dev \ + libjpeg-dev \ + libopenjp2-7-dev \ + libpng-dev \ + libsqlite3-dev \ + libwebp-dev \ + libzstd-dev \ + libproj-dev \ + pkgconf \ + sqlite3 \ + unzip + +WORKDIR /build/jxl +#main from 10/05/2022 +ARG JXLVERSION=ff65c387ac98c7f597ed7e954c28fb32065bf553 +RUN curl -sL https://github.com/libjxl/libjxl/archive/$JXLVERSION.tar.gz -o jxl.tgz &&\ + tar xzf jxl.tgz &&\ + cd libjxl-$JXLVERSION &&\ + ./deps.sh &&\ + mkdir build && cd build &&\ + cmake .. -DBUILD_TESTING=OFF -DCMAKE_BUILD_TYPE=Release &&\ + make -j8 &&\ + make install &&\ + cd /build && rm -rf jxl + + +WORKDIR /build/gdal +ARG GDALVERSION=release/3.5 +RUN curl -sL https://github.com/OSGeo/gdal/archive/$GDALVERSION.tar.gz -o gdal.tar.gz &&\ + tar xzf gdal.tar.gz --strip-components 1 &&\ + mkdir build && cd build &&\ + cmake .. \ + -DOGR_BUILD_OPTIONAL_DRIVERS=OFF \ + -DGDAL_ENABLE_DRIVER_DIMAP=ON \ + -DGDAL_ENABLE_DRIVER_JP2OPENJPEG=ON \ + -DGDAL_ENABLE_DRIVER_WEBP=ON \ + -DGDAL_ENABLE_DRIVER_JPEG=ON \ + -DGDAL_ENABLE_DRIVER_PNG=ON \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_TESTING=OFF \ + -DGDAL_USE_CURL=OFF \ + -DGDAL_USE_SQLITE3=OFF \ + -DGDAL_ENABLE_DRIVER_ISO8211=OFF \ + -DGDAL_BUILD_OPTIONAL_DRIVERS=OFF \ + -DGDAL_USE_JXL=ON \ + -DGDAL_USE_KDU=OFF \ + -DGDAL_USE_TIFF_INTERNAL=ON \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_PYTHON_BINDINGS=OFF \ + -DENABLE_GNM=OFF\ + -DGDAL_USE_XERCESC=OFF &&\ + make -j8 && \ + make install &&\ + ldconfig /usr/local/lib &&\ + cd /build && rm -rf gdal + +WORKDIR /cogger +COPY go.* /cogger/ +COPY vendor.docker vendor +COPY go.* /cogger/ +RUN cd vendor && go build ./... && cd .. +COPY *.go /cogger/ +COPY cmd/tiler/*.go /cogger/cmd/tiler/ +WORKDIR /cogger/cmd/tiler +RUN go build -ldflags "-X main.defaultImage=$COGGER_IMAGE" -o tiler . + +FROM debian:bullseye +RUN apt update && apt install -y \ + libbrotli1 \ + libjpeg62-turbo \ + libopenjp2-7 \ + libpng16-16 \ + libproj19 \ + libsqlite3-0 \ + libwebp6 \ + libzstd1 &&\ + apt clean &&\ + rm -rf /var/lib/apt/lists/* + + +COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt +COPY --from=builder /usr/local/lib/*.so /usr/local/lib/ +COPY --from=builder /usr/local/share/* /usr/local/share/ +RUN ldconfig +COPY --from=builder /cogger/cmd/tiler/tiler /usr/local/bin/tiler + diff --git a/cmd/tiler/Makefile b/cmd/tiler/Makefile new file mode 100644 index 0000000..7d18287 --- /dev/null +++ b/cmd/tiler/Makefile @@ -0,0 +1,12 @@ +IMAGE:=eu.gcr.io/d-dzm-deepzoom/cogger/cogger:20220817 + +build: + @go mod vendor && mv ../../vendor ../../vendor.docker + -docker build -t $(IMAGE) --build-arg COGGER_IMAGE=$(IMAGE) -f Dockerfile ../.. + @rm -rf ../../vendor.docker + +push: + docker push $(IMAGE) + +build-local: + go build -ldflags "-X main.defaultImage=$(IMAGE)" . diff --git a/cmd/tiler/tiler-main.go b/cmd/tiler/tiler-main.go new file mode 100644 index 0000000..3cfac0e --- /dev/null +++ b/cmd/tiler/tiler-main.go @@ -0,0 +1,695 @@ +package main + +import ( + "context" + "fmt" + "io" + "io/ioutil" + "os" + "os/signal" + "strings" + "time" + + "cloud.google.com/go/storage" + "github.com/airbusgeo/cogger" + "github.com/airbusgeo/godal" + "github.com/airbusgeo/osio" + "github.com/airbusgeo/osio/gcs" + "github.com/google/tiff" + "github.com/google/uuid" + "github.com/spf13/cobra" + "github.com/tbonfort/gobs" + adst "go.airbusds-geo.com/gcp/storage" + "go.airbusds-geo.com/log" + k8sv1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + k8smeta "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" + + wfv1 "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1" + "sigs.k8s.io/yaml" +) + +var stcl *storage.Client +var adstcl *adst.Client +var gcsa *osio.Adapter + +var copts []string +var configOpts []string +var verbose bool +var blocksize string +var numCachedBlcocks int +var startTime time.Time +var workBucket string +var width, height int +var ulx, uly, srcWidth, srcHeight float64 +var shell bool +var rpc bool +var mainSwitches string +var ovrSwitches string +var slaveSwitches string +var pixelCount int +var jobid string + +var defaultImage string = "build-error-this-variable-should-have-been-set-on-build" +var dockerImage string + +var rootCmd = &cobra.Command{ + Use: "tiler", + Short: "cog tiling cli", + CompletionOptions: cobra.CompletionOptions{ + DisableDefaultCmd: true, + }, + SilenceUsage: true, + + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + startTime = time.Now() + if !verbose { + os.Setenv("LOGLEVEL", "info") + log.Structured() + } + ctx := cmd.Context() + var err error + + if stcl, err = storage.NewClient(ctx); err != nil { + return fmt.Errorf("storage.newclient: %w", err) + } + if adstcl, err = adst.New(ctx, adst.WithStorageClient(stcl)); err != nil { + return fmt.Errorf("ads storage.new: %w", err) + } + + gcsh, err := gcs.Handle(ctx, gcs.GCSClient(stcl)) + if err != nil { + return fmt.Errorf("gcs.handle: %w", err) + } + gcsa, err = osio.NewAdapter(gcsh, osio.BlockSize(blocksize), osio.NumCachedBlocks(numCachedBlcocks)) + if err != nil { + return fmt.Errorf("osio.new: %w", err) + } + if err := godal.RegisterVSIHandler("gs://", gcsa); err != nil { + return fmt.Errorf("register osio: %w", err) + } + godal.RegisterAll() + return nil + }, + PersistentPostRun: func(cmd *cobra.Command, _ []string) { + log.Logger(cmd.Context()).Sugar().Debugf("command %s took %.1fs", + cmd.Name(), time.Since(startTime).Seconds()) + + }, +} + +func init() { + rootCmd.PersistentFlags().BoolVar(&verbose, "verbose", false, "verbose output") + rootCmd.PersistentFlags().StringVar(&workBucket, "workingBucket", "cogger-scratch", "temporary work bucket") + rootCmd.PersistentFlags().StringVar(&blocksize, "blocksize", "512k", "gs cache blocksize") + rootCmd.PersistentFlags().IntVar(&numCachedBlcocks, "numblocks", 1000, "number of gs cached blocks") + rootCmd.AddCommand(masterCmd, slaveCmd, vrtCmd, coggerCmd) + + masterCmd.Flags().StringArrayVar(&copts, "co", nil, "tif creation options") + masterCmd.Flags().StringArrayVar(&configOpts, "config", nil, "gdal configuration options") + masterCmd.Flags().StringVar(&jobid, "jobID", "", "(advanced) use predefined job identifier") + masterCmd.Flags().StringVar(&mainSwitches, "mainSwitches", "", "gdal_translate switches for main dataset. e.g: \"-b 1 -b 3 -b 2 -a_srs epsg 4326\"") + masterCmd.Flags().StringVar(&ovrSwitches, "ovrSwitches", "", "gdal_translate switches for overview datasets") + masterCmd.Flags().StringVar(&dockerImage, "dockerImage", defaultImage, "docker image for workers") + masterCmd.Flags().BoolVar(&shell, "shell", false, "output shell script instead of argo workflow") + masterCmd.Flags().IntVar(&pixelCount, "pixelCount", 8192*8192, "target number of pixels per strip") + + slaveCmd.Flags().StringArrayVar(&copts, "co", nil, "tif creation options") + slaveCmd.Flags().StringArrayVar(&configOpts, "config", nil, "gdal configuration options") + slaveCmd.Flags().StringVar(&ovrSwitches, "switches", "", "gdal_translate switches") + slaveCmd.Flags().IntVar(&width, "w", 0, "output width") + slaveCmd.MarkFlagRequired("w") + slaveCmd.Flags().IntVar(&height, "h", 0, "output height") + slaveCmd.MarkFlagRequired("h") + slaveCmd.Flags().Float64Var(&ulx, "ulx", 0, "source x origin (in pixels)") + slaveCmd.MarkFlagRequired("ulx") + slaveCmd.Flags().Float64Var(&uly, "uly", 0, "source y origin (in pixels)") + slaveCmd.MarkFlagRequired("uly") + slaveCmd.Flags().Float64Var(&srcWidth, "sw", 0, "source width (in pixels)") + slaveCmd.MarkFlagRequired("sw") + slaveCmd.Flags().Float64Var(&srcHeight, "sh", 0, "source height (in pixels)") + slaveCmd.MarkFlagRequired("sh") + slaveCmd.Flags().BoolVar(&rpc, "rpc", false, "rpc georeferencing") + + coggerCmd.Flags().IntVar(&pixelCount, "pixelCount", 8192*8192, "target number of pixels per strip") + coggerCmd.MarkFlagRequired("pixelCount") + coggerCmd.Flags().IntVar(&width, "w", 0, "output width") + coggerCmd.MarkFlagRequired("w") + coggerCmd.Flags().IntVar(&height, "h", 0, "output height") + coggerCmd.MarkFlagRequired("h") + +} + +func main() { + ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt) + defer stop() + if err := rootCmd.ExecuteContext(ctx); err != nil { + //fmt.Println(err) + os.Exit(1) + } +} + +func int32Ptr(val int32) *int32 { + a := val + return &a +} +func intOrStringPtr(val int) *intstr.IntOrString { + a := intstr.FromInt(val) + return &a +} + +func printCommand(cmd []string) string { + sb := strings.Builder{} + for i, c := range cmd { + if i != 0 { + fmt.Fprintf(&sb, " ") + } + fmt.Fprintf(&sb, "%q", c) + } + return sb.String() +} + +func resourcePtr(val string) *resource.Quantity { + res := resource.MustParse(val) + return &res +} + +var masterCmd = &cobra.Command{ + Use: "master gs://bucket/dstcog.tif srcfile", + Short: "create workflow for cogifying srcfile to cog on gs://", + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + dstDatasetName := args[0] + srcDatasetName := args[1] + + if jobid == "" { + jobid = uuid.New().String() + } + + srcDataset, err := godal.Open(srcDatasetName, godal.RasterOnly()) + if err != nil { + return fmt.Errorf("open %s: %w", srcDatasetName, err) + } + defer srcDataset.Close() + srcStruct := srcDataset.Structure() + + rpcdata := srcDataset.Metadatas(godal.Domain("RPC")) + if len(rpcdata) > 0 { + rpc = true + } + + tiler, err := cogger.NewTiler(srcStruct.SizeX, srcStruct.SizeY, cogger.TargetPixelCount(pixelCount)) + if err != nil { + return fmt.Errorf("newtiler: %w", err) + } + + pyramid := tiler.Tiling() + + srcfile := srcDatasetName + + wf := &wfv1.Workflow{ + ObjectMeta: k8smeta.ObjectMeta{ + GenerateName: "cogger-", + }, + TypeMeta: k8smeta.TypeMeta{ + APIVersion: "argoproj.io/v1alpha1", + Kind: "Workflow", + }, + Spec: wfv1.WorkflowSpec{ + TTLStrategy: &wfv1.TTLStrategy{ + SecondsAfterSuccess: int32Ptr(3600), + }, + Entrypoint: "cogger", + TemplateDefaults: &wfv1.Template{ + Volumes: []k8sv1.Volume{ + { + Name: "scratch", + VolumeSource: k8sv1.VolumeSource{ + EmptyDir: &k8sv1.EmptyDirVolumeSource{ + SizeLimit: resourcePtr("200M"), + }, + }, + }, + }, + Container: &k8sv1.Container{ + ImagePullPolicy: k8sv1.PullAlways, + Resources: k8sv1.ResourceRequirements{ + Requests: k8sv1.ResourceList{ + k8sv1.ResourceCPU: resource.MustParse("2"), + k8sv1.ResourceMemory: resource.MustParse("1G"), + }, + }, + WorkingDir: "/scratch", + VolumeMounts: []k8sv1.VolumeMount{ + { + Name: "scratch", + MountPath: "/scratch", + }, + }, + }, + }, + Templates: []wfv1.Template{ + {Name: "cogger"}, + }, + }, + } + + zstrips := [][]string{} + var z int + var img cogger.Image + var lastCommands [][]string + for z, img = range pyramid { + if z > 0 { + rpc = false + } + strips := []string{} + slaveCommands := [][]string{} + for s, strip := range img.Strips { + stripfile := fmt.Sprintf("gs://%s/%s/%d-%d.tif", workBucket, jobid, z, s) + command := []string{"tiler", "slave", stripfile, srcfile, + "--w", fmt.Sprintf("%d", strip.TargetWidth), + "--h", fmt.Sprintf("%d", strip.TargetHeight), + "--ulx", fmt.Sprintf("%g", strip.SrcTopLeftX), + "--uly", fmt.Sprintf("%g", strip.SrcTopLeftY), + "--sw", fmt.Sprintf("%g", strip.SrcBottomRightX-strip.SrcTopLeftX), + "--sh", fmt.Sprintf("%g", strip.SrcBottomRightY-strip.SrcTopLeftY), + fmt.Sprintf("--rpc=%v", rpc)} + for _, co := range copts { + command = append(command, "--co", co) + } + for _, co := range configOpts { + command = append(command, "--config", co) + } + if z == 0 && mainSwitches != "" { + if _, err = getSwitches(mainSwitches, false); err != nil { + return err + } + command = append(command, "--switches", mainSwitches) + } else if ovrSwitches != "" { + if _, err = getSwitches(ovrSwitches, true); err != nil { + return err + } + command = append(command, "--switches", ovrSwitches) + } + slaveCommands = append(slaveCommands, command) + strips = append(strips, stripfile) + if shell { + fmt.Println(printCommand(command)) + } + } + zstrips = append(zstrips, strips) + ps := wfv1.ParallelSteps{} + if len(slaveCommands) > 1 { + for s, sl := range slaveCommands { + sstep := wfv1.WorkflowStep{ + Name: fmt.Sprintf("Strip-Z%d-%d", z, s), + Inline: &wfv1.Template{ + RetryStrategy: &wfv1.RetryStrategy{ + Limit: intOrStringPtr(5), + }, + Container: &k8sv1.Container{ + Name: "slave", + Image: dockerImage, + Command: sl, + }, + }, + } + ps.Steps = append(ps.Steps, sstep) + /* + fmt.Printf("gdal_translate -co TILED=YES -co COMPRESS=JPEG -outsize %d %d -srcwin %g %g %g %g %s %s\n", + strip.TargetWidth, strip.TargetHeight, + strip.SrcTopLeftX, strip.SrcTopLeftY, + strip.SrcBottomRightX-strip.SrcTopLeftX, strip.SrcBottomRightY-strip.SrcTopLeftY, + srcfile, stripfile) + */ + } + wf.Spec.Templates[0].Steps = append(wf.Spec.Templates[0].Steps, ps) + } else { + lastCommands = append(lastCommands, slaveCommands...) + } + + vrtname := fmt.Sprintf("gs://%s/%s/vrt-z%d.vrt", workBucket, jobid, z) + command := []string{"tiler", "vrt", vrtname} + for _, strip := range strips { + if rpc { + strip += ".vrt" + } + command = append(command, strip) + } + if shell { + fmt.Println(printCommand(command)) + } + if len(slaveCommands) > 1 { + step := wfv1.WorkflowStep{ + Name: fmt.Sprintf("VRT-Z%d", z), + Inline: &wfv1.Template{ + Container: &k8sv1.Container{ + Name: "vrt", + Image: dockerImage, + Command: command, + }, + RetryStrategy: &wfv1.RetryStrategy{ + Limit: intOrStringPtr(5), + }, + }, + } + wf.Spec.Templates[0].Steps = append(wf.Spec.Templates[0].Steps, + wfv1.ParallelSteps{ + Steps: []wfv1.WorkflowStep{step}, + }) + } else { + lastCommands = append(lastCommands, command) + } + srcfile = vrtname + } + if len(lastCommands) > 0 { + source := "set -e\n" + for _, lc := range lastCommands { + source += fmt.Sprintf("%s\n", printCommand(lc)) + } + step := wfv1.WorkflowStep{ + Name: "lastCommands", + Inline: &wfv1.Template{ + Metadata: wfv1.Metadata{ + Annotations: map[string]string{ + "cluster-autoscaler.kubernetes.io/safe-to-evict": "false", + }, + }, + Script: &wfv1.ScriptTemplate{ + Container: k8sv1.Container{ + Name: "lastCommands", + Image: dockerImage, + Command: []string{"sh"}, + }, + Source: source, + }, + RetryStrategy: &wfv1.RetryStrategy{ + Limit: intOrStringPtr(5), + }, + }, + } + wf.Spec.Templates[0].Steps = append(wf.Spec.Templates[0].Steps, + wfv1.ParallelSteps{ + Steps: []wfv1.WorkflowStep{step}, + }) + } + step := wfv1.WorkflowStep{ + Name: "cogify", + Inline: &wfv1.Template{ + RetryStrategy: &wfv1.RetryStrategy{ + Limit: intOrStringPtr(5), + }, + Metadata: wfv1.Metadata{ + Annotations: map[string]string{ + "cluster-autoscaler.kubernetes.io/safe-to-evict": "false", + }, + }, + Container: &k8sv1.Container{ + Name: "cogify", + Image: dockerImage, + Command: []string{"tiler", "cogify", + "--w", fmt.Sprintf("%d", srcStruct.SizeX), + "--h", fmt.Sprintf("%d", srcStruct.SizeY), + "--pixelCount", fmt.Sprintf("%d", pixelCount), + dstDatasetName}, + Resources: k8sv1.ResourceRequirements{ + Requests: k8sv1.ResourceList{ + k8sv1.ResourceCPU: resource.MustParse("1"), + k8sv1.ResourceMemory: resource.MustParse("4G"), + }, + }, + }, + }, + } + for _, zs := range zstrips { + step.Inline.Container.Command = append(step.Inline.Container.Command, strings.Join(zs, ",")) + } + if shell { + fmt.Println(printCommand(step.Inline.Container.Command)) + } + wf.Spec.Templates[0].Steps = append(wf.Spec.Templates[0].Steps, + wfv1.ParallelSteps{ + Steps: []wfv1.WorkflowStep{step}, + }) + if !shell { + yb, err := yaml.Marshal(wf) + if err != nil { + panic(err) + } + fmt.Println(string(yb)) + } + + return nil + }, +} + +//splits string into switches and returns an error if invalid switches have been passed +func getSwitches(sw string, isOvr bool) ([]string, error) { + switches := []string{} + resamplingProvided := false + for _, s := range strings.Split(sw, " ") { + switch s { + case "-te", "-outsize", "-tr", "-srcwin", "-projwin", "-a_ullr": + return nil, fmt.Errorf("%s switch not allowed", s) + case "-b": + if isOvr { + return nil, fmt.Errorf("%s switch not allowed for overviews", s) + } + case "-r": + resamplingProvided = true + default: + switches = append(switches, s) + } + } + if !resamplingProvided { + switches = append(switches, "-r", "bilinear") + } + return switches, nil +} + +var slaveCmd = &cobra.Command{ + Use: "slave srcfile dstfile", + Short: "extract strip from srcfile and save to dstfile", + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + dstDatasetName := args[0] + srcDatasetName := args[1] + + creationOptions := map[string]string{ + "TILED": "YES", + "COMPRESS": "LZW", + "BLOCKXSIZE": "256", + "BLOCKYSIZE": "256", + "NUM_THREADS": "4", + } + for _, co := range copts { + idx := strings.Index(co, "=") + if idx > 0 { + creationOptions[co[0:idx]] = co[idx+1:] + } + } + coptstring := []string{} + for k, v := range creationOptions { + coptstring = append(coptstring, k+"="+v) + } + + srcDataset, err := godal.Open(srcDatasetName, godal.RasterOnly()) + if err != nil { + return fmt.Errorf("open %s: %w", srcDatasetName, err) + } + defer srcDataset.Close() + + tmpDataset, _ := ioutil.TempFile(".", "strip-*.tif") + tmpDataset.Close() + tmpDatasetName := tmpDataset.Name() + defer os.Remove(tmpDatasetName) + + switches, err := getSwitches(slaveSwitches, (srcWidth != float64(width))) + if err != nil { + return err + } + switches = append(switches, + "-outsize", fmt.Sprintf("%d", width), fmt.Sprintf("%d", height), + "-srcwin", + fmt.Sprintf("%g", ulx), + fmt.Sprintf("%g", uly), + fmt.Sprintf("%g", srcWidth), + fmt.Sprintf("%g", srcHeight)) + dstDS, err := srcDataset.Translate(tmpDatasetName, switches, + godal.CreationOption(coptstring...), + godal.ConfigOption(configOpts...)) + if err != nil { + return fmt.Errorf("open %s: %w", srcDatasetName, err) + } + + if err = dstDS.Close(); err != nil { + return fmt.Errorf("close temp tif: %w", err) + } + stripReader, err := os.Open(tmpDatasetName) + if err != nil { + return fmt.Errorf("failed to reopen %s: %w", tmpDatasetName, err) + } + var cogw io.WriteCloser + if strings.HasPrefix(dstDatasetName, "gs://") { + b, o, err := adst.Parse(dstDatasetName) + if err != nil { + return fmt.Errorf("invalid dst %s: %w", dstDatasetName, err) + } + cogw = stcl.Bucket(b).Object(o).NewWriter(ctx) + } else { + if cogw, err = os.Create(dstDatasetName); err != nil { + return fmt.Errorf("create %s: %w", dstDatasetName, err) + } + } + if err = cogger.DefaultConfig().Rewrite(cogw, stripReader); err != nil { + return fmt.Errorf("cogify strip: %w", err) + } + if err = cogw.Close(); err != nil { + return fmt.Errorf("close %s: %w", dstDatasetName, err) + } + if rpc { + tmpDatasetName += ".vrt" + defer os.Remove(tmpDatasetName) + ds, err := godal.Open(dstDatasetName) + if err != nil { + return fmt.Errorf("failed to reopen %s: %w", dstDatasetName, err) + } + defer ds.Close() + vrtds, err := ds.Translate(tmpDatasetName, nil, godal.VRT) + if err != nil { + return fmt.Errorf("trn to vrt: %w", err) + } + vrtds.ClearMetadata(godal.Domain("RPC")) + vrtds.SetGeoTransform([6]float64{ulx, srcWidth / float64(width), 0, -uly, 0, -srcHeight / float64(height)}) + if err = vrtds.Close(); err != nil { + return fmt.Errorf("close vrt: %w", err) + } + if strings.HasPrefix(dstDatasetName, "gs://") { + if err = adstcl.UploadFromFile(ctx, dstDatasetName+".vrt", tmpDatasetName); err != nil { + return fmt.Errorf("upload %s: %w", dstDatasetName+".vrt", err) + } + } else { + if err = os.Rename(tmpDatasetName, dstDatasetName+".vrt"); err != nil { + return fmt.Errorf("rename vrt to %s: %w", dstDatasetName, err) + } + } + } + return nil + }, +} + +func gdalPreload(datasets []string) error { + pool := gobs.NewPool(25) + batch := pool.Batch() + for _, dsn := range datasets { + dsn := dsn + batch.Submit(func() error { + ds, err := godal.Open(dsn) + if err != nil { + return err + } + ds.Close() + return nil + }) + } + if err := batch.Wait(); err != nil { + return err + } + return nil +} + +var vrtCmd = &cobra.Command{ + Use: "vrt gs://bucket/vrtfile.vrt gs://bucket/srcstrips.tif...", + Short: "create vrtfile from srcfiles", + Args: cobra.MinimumNArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + dstDatasetName := args[0] + tmpDataset, _ := ioutil.TempFile(".", "strips-*.vrt") + tmpDataset.Close() + tmpDatasetName := tmpDataset.Name() + defer os.Remove(tmpDatasetName) + + if err := gdalPreload(args[1:]); err != nil { + return err + } + + dstDS, err := godal.BuildVRT(tmpDatasetName, args[1:], nil) + if err != nil { + return fmt.Errorf("create vrt: %w", err) + } + if err = dstDS.Close(); err != nil { + return fmt.Errorf("close temp vrt: %w", err) + } + if strings.HasPrefix(args[0], "gs://") { + if err = adstcl.UploadFromFile(ctx, dstDatasetName, tmpDatasetName); err != nil { + return fmt.Errorf("upload: %w", err) + } + } else { + if err = os.Rename(tmpDatasetName, dstDatasetName); err != nil { + return fmt.Errorf("rename %s->%s: %w", tmpDatasetName, dstDatasetName, err) + } + } + return nil + }, +} +var coggerCmd = &cobra.Command{ + Use: "cogify cogfile.tif strip-z0-0.tif,strip-z0-1.tif,... strip-z1-0.tif,strip-z1-1.tif,... ...", + Short: "create cog from strips", + Args: cobra.MinimumNArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + dstDatasetName := args[0] + tiler, err := cogger.NewTiler(width, height, cogger.TargetPixelCount(pixelCount), + cogger.PreloadTiles(300)) + if err != nil { + return fmt.Errorf("newtiler: %w", err) + } + pyramid := tiler.Tiling() + readers := make([][]tiff.ReadAtReadSeeker, len(pyramid)) + if len(pyramid) != len(args)-1 { + return fmt.Errorf("supplied strip pyramid %d does not match expected %d", len(args)-1, len(pyramid)) + } + for c := range args[1:] { + stripnames := strings.Split(args[c+1], ",") + if len(pyramid[c].Strips) != len(stripnames) { + return fmt.Errorf("level %d has %d strip sources, expecting %d", c, + len(stripnames), len(pyramid[c].Strips)) + } + if err := gdalPreload(stripnames); err != nil { + return err + } + stripreaders := make([]tiff.ReadAtReadSeeker, len(stripnames)) + for sr, stripname := range stripnames { + if stripreaders[sr], err = gcsa.Reader(stripname); err != nil { + return fmt.Errorf("open %s: %w", stripname, err) + } + } + readers[c] = stripreaders + } + + var cogw io.WriteCloser + if strings.HasPrefix(dstDatasetName, "gs://") { + cogbucket, cogobject, err := adst.Parse(dstDatasetName) + if err != nil { + return fmt.Errorf("invalid dst %s: %w", dstDatasetName, err) + } + cogw = stcl.Bucket(cogbucket).Object(cogobject).NewWriter(ctx) + } else { + cogw, err = os.Create(dstDatasetName) + if err != nil { + return fmt.Errorf("create %s: %w", dstDatasetName, err) + } + } + + if err := tiler.AssembleStrips(cogw, readers); err != nil { + return fmt.Errorf("tiler.assemble: %w", err) + } + if err := cogw.Close(); err != nil { + return fmt.Errorf("close %s: %w", dstDatasetName, err) + } + return nil + }, +} diff --git a/cog.go b/cog.go index 65e60e7..0251619 100644 --- a/cog.go +++ b/cog.go @@ -6,75 +6,49 @@ import ( "fmt" "io" - "github.com/google/tiff" _ "github.com/google/tiff/bigtiff" ) -type subfileType uint32 - const ( subfileTypeNone = 0 subfileTypeReducedImage = 1 - subfileTypePage = 2 - subfileTypeMask = 4 -) - -type planarConfiguration uint16 - -const ( - planarConfigurationContig = 1 - planarConfigurationSeparate = 2 -) - -type predictor uint16 - -const ( - predictorNone = 1 - predictorHorizontal = 2 - predictorFloatingPoint = 3 -) - -type sampleFormat uint16 - -const ( - sampleFormatUInt = 1 - sampleFormatInt = 2 - sampleFormatIEEEFP = 3 - sampleFormatVoid = 4 - sampleFormatComplexInt = 5 - sampleFormatComplexIEEEFP = 6 -) - -type extraSamples uint16 - -const ( - extraSamplesUnspecified = 0 - extraSamplesAssocAlpha = 1 - extraSamplesUnassAlpha = 2 + //subfileTypePage = 2 + subfileTypeMask = 4 ) -type photometricInterpretation uint16 - -const ( - photometricInterpretationMinIsWhite = 0 - photometricInterpretationMinIsBlack = 1 - photometricInterpretationRGB = 2 - photometricInterpretationPalette = 3 - photometricInterpretationMask = 4 - photometricInterpretationSeparated = 5 - photometricInterpretationYCbCr = 6 - photometricInterpretationCIELab = 8 - photometricInterpretationICCLab = 9 - photometricInterpretationITULab = 10 - photometricInterpretationLOGL = 32844 - photometricInterpretationLOGLUV = 32845 -) - -type ifd struct { - //Any field added here should also be accounted for in WriteIFD and ifd.Fieldcount +// PlanarInterleaving describes how the band data should be interleaved for tiffs +// with more than 1 plane and with PlanarConfiguration=2 +// +// This is an advanced usage option that does not modify the actual image data +// but tweaks the order in which each plane's (i.e. band's) tile is written +// in the resulting file. +// +// Examples for a 3-band rgb image: +// +// - [[0,1,2]] will result in tiles written in the order r1,g1,b1,r2,g2,b2...rn,gn,bn. This +// is the default. +// - [0],[1],[2]] => r1,r2...rn,g1,g2....gn,b1,b2...bn +// - [0],[2],[1]] => r1,r2...rn,b1,b2....bn,g1,g2...gn +// - [0,1],[2]] => r1,g1,r2,g2...rn,gn,b1,b2....bn +// +// Examples for a 3-band rgb image with mask: +// +// - [[0,1,2,3]] will result in tiles written in the order r1,g1,b1,m1,r2,g2,b2,m2...rn,gn,bn,mn. This +// is the default. +// - [0],[1],[2],[3]] => r1,r2...rn,g1,g2...gn,b1,b2...bn,m1,m2...mn +// - [0],[3],[2],[1]] => r1,r2...rn,m1,m2...m3,b1,b2...bn,g1,g2...gn +// - [0,1],[2],[3]] => r1,g1,r2,g2...rn,gn,b1,b2....bn,m1m2...mn +// +// For a n-band image, each band index from 0 to n-1 must appear exactly once +// in the array. If the image also has a mask, the index n must also appear exactly +// once and represents the mask position. +type PlanarInterleaving [][]int + +type IFD struct { + //Any field added here should also be accounted for in computeStructure and writeIFD SubfileType uint32 `tiff:"field,tag=254"` ImageWidth uint64 `tiff:"field,tag=256"` - ImageLength uint64 `tiff:"field,tag=257"` + ImageHeight uint64 `tiff:"field,tag=257"` BitsPerSample []uint16 `tiff:"field,tag=258"` Compression uint16 `tiff:"field,tag=259"` PhotometricInterpretation uint16 `tiff:"field,tag=262"` @@ -85,12 +59,9 @@ type ifd struct { Predictor uint16 `tiff:"field,tag=317"` Colormap []uint16 `tiff:"field,tag=320"` TileWidth uint16 `tiff:"field,tag=322"` - TileLength uint16 `tiff:"field,tag=323"` - OriginalTileOffsets []uint64 `tiff:"field,tag=324"` - NewTileOffsets64 []uint64 - NewTileOffsets32 []uint32 - TempTileByteCounts []uint64 `tiff:"field,tag=325"` - TileByteCounts []uint32 + TileHeight uint16 `tiff:"field,tag=323"` + TileOffsets []uint64 `tiff:"field,tag=324"` + TileByteCounts []uint64 `tiff:"field,tag=325"` ExtraSamples []uint16 `tiff:"field,tag=338"` SampleFormat []uint16 `tiff:"field,tag=339"` JPEGTables []byte `tiff:"field,tag=347"` @@ -105,34 +76,110 @@ type ifd struct { NoData string `tiff:"field,tag=42113"` LERCParams []uint32 `tiff:"field,tag=50674"` RPCs []float64 `tiff:"field,tag=50844"` + LoadTile func(idx int, data []byte) error + + mask *IFD //Optional single-plane mask. Mask.Mask and Mask.Overviews must be nil + overviews []*IFD //Optional overviews, sorted from largest to smallest. Overviews.Overviews must be nil. + newTileOffsets []uint64 + ntags int + tagSize int + strileSize int + planarInterleaving PlanarInterleaving +} - overview *ifd - masks []*ifd - - ntags uint64 - ntilesx, ntilesy uint64 - nplanes uint64 //1 if PlanarConfiguration==1, SamplesPerPixel if PlanarConfiguration==2 - tagsSize uint64 - strileSize uint64 - r tiff.BReader +func (ifd *IFD) NTilesX() int { + return (int(ifd.ImageWidth) + int(ifd.TileWidth) - 1) / int(ifd.TileWidth) +} +func (ifd *IFD) NTilesY() int { + return (int(ifd.ImageHeight) + int(ifd.TileHeight) - 1) / int(ifd.TileHeight) +} +func (ifd *IFD) NPlanes() int { + planeCount := 1 + if ifd.PlanarConfiguration == 2 { + planeCount = int(ifd.SamplesPerPixel) + } + return planeCount +} +func (ifd *IFD) TileIdx(x, y, plane int) int { + nx, ny := ifd.NTilesX(), ifd.NTilesY() + return int(nx*ny*plane + y*nx + x) +} +func (ifd *IFD) TileFromIdx(idx int) (x, y, plane int) { + nx, ny := ifd.NTilesX(), ifd.NTilesY() + psize := nx * ny + plane = idx / psize + pidx := idx % psize + x = pidx % nx + y = pidx / nx + return } -/* -func (ifd *IFD) TagCount() uint64 { - s, _, _ := ifd.Structure() - return s +func (ifd *IFD) tileLen(idx int) int { + return int(ifd.TileByteCounts[idx]) } -func (ifd *IFD) TagsSize() uint64 { - _, s, _ := ifd.Structure() - return s + +// SetPlanarInterleaving configures a non-default planar interleaving +// for this ifd. Must be called after AddMask. +func (ifd *IFD) SetPlanarInterleaving(pi PlanarInterleaving) error { + if ifd.PlanarConfiguration != 2 { + return fmt.Errorf("ifd is not PLANARCONFIG_SEPARATE") + } + n := int(ifd.SamplesPerPixel) + if ifd.mask != nil { + n++ + } + check := make([]bool, n) + for _, l1 := range pi { + for _, l2 := range l1 { + if l2 < 0 || l2 >= n || check[l2] { + return fmt.Errorf("invalid/duplicate entry %d", l2) + } + check[l2] = true + } + } + for i, l2 := range check { + if !l2 { + return fmt.Errorf("missing entry %d", i) + } + } + //deep copy + for _, l1 := range pi { + l2 := append([]int{}, l1...) + ifd.planarInterleaving = append(ifd.planarInterleaving, l2) + } + return nil } -func (ifd *IFD) StrileSize() uint64 { - _, _, s := ifd.Structure() - return s + +func (ifd *IFD) setDefaultPlanarInterleaving() { + if ifd.planarInterleaving != nil { + return + } + if ifd.NPlanes() == 1 { + if ifd.mask != nil { + ifd.planarInterleaving = [][]int{{0, 1}} + } else { + ifd.planarInterleaving = [][]int{{0}} + } + return + } + n := int(ifd.SamplesPerPixel) + if ifd.mask != nil { + n++ + } + ns := make([]int, n) + for i := range ns { + ns[i] = i + } + err := ifd.SetPlanarInterleaving(append([][]int{}, ns)) + if err != nil { + panic(err) //bug + } } -*/ -func (ifd *ifd) AddOverview(ovr *ifd) { +func (ifd *IFD) AddOverview(ovr *IFD) error { + if len(ovr.overviews) > 0 { + return fmt.Errorf("cannot add overview with embedded overview") + } ovr.SubfileType = subfileTypeReducedImage ovr.ModelPixelScaleTag = nil ovr.ModelTiePointTag = nil @@ -140,11 +187,51 @@ func (ifd *ifd) AddOverview(ovr *ifd) { ovr.GeoAsciiParamsTag = "" ovr.GeoDoubleParamsTag = nil ovr.GeoKeyDirectoryTag = nil - ifd.overview = ovr + ovr.GDALMetaData = "" + ovr.RPCs = nil + idx := 0 + for idx = range ifd.overviews { + if ifd.overviews[idx].ImageWidth > ovr.ImageWidth && + ifd.overviews[idx].ImageHeight > ovr.ImageHeight { + idx++ + continue + } + break + } + prev := ifd + if len(ifd.overviews) > 0 { + prev = ifd.overviews[len(ifd.overviews)-1] + } + if prev.ImageWidth <= ovr.ImageWidth || + prev.ImageHeight <= ovr.ImageHeight { + return fmt.Errorf("invalid overview size") + } + if prev.SamplesPerPixel != ovr.SamplesPerPixel || + len(prev.BitsPerSample) != len(ovr.BitsPerSample) { + return fmt.Errorf("invalid band count") + } + ifd.overviews = append(ifd.overviews, nil) + copy(ifd.overviews[idx+1:], ifd.overviews[idx:]) + if ovr.mask != nil { + ovr.mask.SubfileType = subfileTypeMask | subfileTypeReducedImage + } + ifd.overviews[idx] = ovr + return nil } -func (ifd *ifd) AddMask(msk *ifd) error { - if len(msk.masks) > 0 || msk.overview != nil { - return fmt.Errorf("cannot add mask with overviews or masks") + +// AddMask sets msk as a mask for ifd. Must not be called after SetPlanarInterleaving +func (ifd *IFD) AddMask(msk *IFD) error { + if msk.mask != nil || len(msk.overviews) > 0 { + return fmt.Errorf("cannot add mask containing overviews or mask") + } + if len(ifd.planarInterleaving) > 0 { + return fmt.Errorf("AddMask must be called before calling SetPlanarInterleaving") + } + if msk.ImageWidth != ifd.ImageWidth || msk.ImageHeight != ifd.ImageHeight || + msk.TileWidth != ifd.TileWidth || msk.TileHeight != ifd.TileHeight || + msk.SamplesPerPixel != 1 || len(msk.BitsPerSample) != 1 || + len(msk.TileByteCounts) != len(ifd.TileByteCounts)/ifd.NPlanes() { + return fmt.Errorf("incompatible mask structure") } switch ifd.SubfileType { case subfileTypeNone: @@ -152,7 +239,7 @@ func (ifd *ifd) AddMask(msk *ifd) error { case subfileTypeReducedImage: msk.SubfileType = subfileTypeMask | subfileTypeReducedImage default: - return fmt.Errorf("invalid subfiledtype") + return fmt.Errorf("invalid parent subfiletype") } msk.ModelPixelScaleTag = nil msk.ModelTiePointTag = nil @@ -160,172 +247,221 @@ func (ifd *ifd) AddMask(msk *ifd) error { msk.GeoAsciiParamsTag = "" msk.GeoDoubleParamsTag = nil msk.GeoKeyDirectoryTag = nil - ifd.masks = append(ifd.masks, msk) + msk.GDALMetaData = "" + msk.RPCs = nil + ifd.mask = msk return nil } -func (ifd *ifd) structure(bigtiff bool) (tagCount, ifdSize, strileSize, planeCount uint64) { - cnt := uint64(0) - size := uint64(16) //8 for field count + 8 for next ifd offset - tagSize := uint64(20) - planeCount = 1 - if !bigtiff { - size = 6 // 2 for field count + 4 for next ifd offset +const ( + tByte = 1 + tAscii = 2 + tShort = 3 + tLong = 4 + //tRational = 5 + tSByte = 6 + //tUndefined = 7 + tSShort = 8 + tSLong = 9 + //tSRational = 10 + tFloat = 11 + tDouble = 12 + tLong8 = 16 + tSLong8 = 17 + //tIFD8 = 18 +) + +func (cog *cog) computeStructure(ifd *IFD) { + ifd.ntags = 0 + ifd.tagSize = 16 //8 for field count + 8 for next ifd offset + ifd.strileSize = 0 + tagSize := 20 + if !cog.bigtiff { + ifd.tagSize = 6 // 2 for field count + 4 for next ifd offset tagSize = 12 } - strileSize = uint64(0) - if ifd.SubfileType > 0 { - cnt++ - size += tagSize + ifd.ntags++ + ifd.tagSize += tagSize } if ifd.ImageWidth > 0 { - cnt++ - size += tagSize + ifd.ntags++ + ifd.tagSize += tagSize } - if ifd.ImageLength > 0 { - cnt++ - size += tagSize + if ifd.ImageHeight > 0 { + ifd.ntags++ + ifd.tagSize += tagSize } if len(ifd.BitsPerSample) > 0 { - cnt++ - size += arrayFieldSize(ifd.BitsPerSample, bigtiff) + ifd.ntags++ + ifd.tagSize += arrayFieldSize(ifd.BitsPerSample, cog.bigtiff) } if ifd.Compression > 0 { - cnt++ - size += tagSize + ifd.ntags++ + ifd.tagSize += tagSize } - cnt++ /*PhotometricInterpretation*/ - size += tagSize + ifd.ntags++ /*PhotometricInterpretation*/ + ifd.tagSize += tagSize if len(ifd.DocumentName) > 0 { - cnt++ - size += arrayFieldSize(ifd.DocumentName, bigtiff) + ifd.ntags++ + ifd.tagSize += arrayFieldSize(ifd.DocumentName, cog.bigtiff) } if ifd.SamplesPerPixel > 0 { - cnt++ - size += tagSize + ifd.ntags++ + ifd.tagSize += tagSize } if ifd.PlanarConfiguration > 0 { - cnt++ - size += tagSize - } - if ifd.PlanarConfiguration == 2 { - planeCount = uint64(ifd.SamplesPerPixel) + ifd.ntags++ + ifd.tagSize += tagSize } if len(ifd.DateTime) > 0 { - cnt++ - size += arrayFieldSize(ifd.DateTime, bigtiff) + ifd.ntags++ + ifd.tagSize += arrayFieldSize(ifd.DateTime, cog.bigtiff) } if ifd.Predictor > 0 { - cnt++ - size += tagSize + ifd.ntags++ + ifd.tagSize += tagSize } if len(ifd.Colormap) > 0 { - cnt++ - size += arrayFieldSize(ifd.Colormap, bigtiff) + ifd.ntags++ + ifd.tagSize += arrayFieldSize(ifd.Colormap, cog.bigtiff) } if ifd.TileWidth > 0 { - cnt++ - size += tagSize - } - if ifd.TileLength > 0 { - cnt++ - size += tagSize - } - if len(ifd.NewTileOffsets32) > 0 { - cnt++ - size += tagSize - strileSize += arrayFieldSize(ifd.NewTileOffsets32, bigtiff) - tagSize - } else if len(ifd.NewTileOffsets64) > 0 { - cnt++ - size += tagSize - strileSize += arrayFieldSize(ifd.NewTileOffsets64, bigtiff) - tagSize + ifd.ntags++ + ifd.tagSize += tagSize + } + if ifd.TileHeight > 0 { + ifd.ntags++ + ifd.tagSize += tagSize } + //ignore original offsets, get the number of them from the TileByteCounts if len(ifd.TileByteCounts) > 0 { - cnt++ - size += tagSize - strileSize += arrayFieldSize(ifd.TileByteCounts, bigtiff) - tagSize + ifd.ntags++ + ifd.tagSize += tagSize + if cog.bigtiff { + ifd.strileSize += arrayFieldSize(ifd.TileByteCounts, cog.bigtiff) - tagSize + } else { + ifd.strileSize += arrayFieldSize32(ifd.TileByteCounts, cog.bigtiff) - tagSize + } + } + if len(ifd.TileByteCounts) > 0 { + ifd.ntags++ + ifd.tagSize += tagSize + ifd.strileSize += arrayFieldSize32(ifd.TileByteCounts, cog.bigtiff) - tagSize } if len(ifd.ExtraSamples) > 0 { - cnt++ - size += arrayFieldSize(ifd.ExtraSamples, bigtiff) + ifd.ntags++ + ifd.tagSize += arrayFieldSize(ifd.ExtraSamples, cog.bigtiff) } if len(ifd.SampleFormat) > 0 { - cnt++ - size += arrayFieldSize(ifd.SampleFormat, bigtiff) + ifd.ntags++ + ifd.tagSize += arrayFieldSize(ifd.SampleFormat, cog.bigtiff) } if len(ifd.JPEGTables) > 0 { - cnt++ - size += arrayFieldSize(ifd.JPEGTables, bigtiff) + ifd.ntags++ + ifd.tagSize += arrayFieldSize(ifd.JPEGTables, cog.bigtiff) } if len(ifd.ModelPixelScaleTag) > 0 { - cnt++ - size += arrayFieldSize(ifd.ModelPixelScaleTag, bigtiff) + ifd.ntags++ + ifd.tagSize += arrayFieldSize(ifd.ModelPixelScaleTag, cog.bigtiff) } if len(ifd.ModelTiePointTag) > 0 { - cnt++ - size += arrayFieldSize(ifd.ModelTiePointTag, bigtiff) + ifd.ntags++ + ifd.tagSize += arrayFieldSize(ifd.ModelTiePointTag, cog.bigtiff) } if len(ifd.ModelTransformationTag) > 0 { - cnt++ - size += arrayFieldSize(ifd.ModelTransformationTag, bigtiff) + ifd.ntags++ + ifd.tagSize += arrayFieldSize(ifd.ModelTransformationTag, cog.bigtiff) } if len(ifd.GeoKeyDirectoryTag) > 0 { - cnt++ - size += arrayFieldSize(ifd.GeoKeyDirectoryTag, bigtiff) + ifd.ntags++ + ifd.tagSize += arrayFieldSize(ifd.GeoKeyDirectoryTag, cog.bigtiff) } if len(ifd.GeoDoubleParamsTag) > 0 { - cnt++ - size += arrayFieldSize(ifd.GeoDoubleParamsTag, bigtiff) + ifd.ntags++ + ifd.tagSize += arrayFieldSize(ifd.GeoDoubleParamsTag, cog.bigtiff) } if ifd.GeoAsciiParamsTag != "" { - cnt++ - size += arrayFieldSize(ifd.GeoAsciiParamsTag, bigtiff) + ifd.ntags++ + ifd.tagSize += arrayFieldSize(ifd.GeoAsciiParamsTag, cog.bigtiff) } if ifd.GDALMetaData != "" { - cnt++ - size += arrayFieldSize(ifd.GDALMetaData, bigtiff) + ifd.ntags++ + ifd.tagSize += arrayFieldSize(ifd.GDALMetaData, cog.bigtiff) } if ifd.NoData != "" { - cnt++ - size += arrayFieldSize(ifd.NoData, bigtiff) + ifd.ntags++ + ifd.tagSize += arrayFieldSize(ifd.NoData, cog.bigtiff) } if len(ifd.LERCParams) > 0 { - cnt++ - size += arrayFieldSize(ifd.LERCParams, bigtiff) + ifd.ntags++ + ifd.tagSize += arrayFieldSize(ifd.LERCParams, cog.bigtiff) } if len(ifd.RPCs) > 0 { - cnt++ - size += arrayFieldSize(ifd.RPCs, bigtiff) + ifd.ntags++ + ifd.tagSize += arrayFieldSize(ifd.RPCs, cog.bigtiff) } - return cnt, size, strileSize, planeCount } type tagData struct { bytes.Buffer - Offset uint64 + Offset int } -func (t *tagData) NextOffset() uint64 { - return t.Offset + uint64(t.Buffer.Len()) +func (t *tagData) NextOffset() int { + return t.Offset + t.Buffer.Len() } -type cog struct { - enc binary.ByteOrder - ifd *ifd - bigtiff bool +type Config struct { + //Encoding selects big or little endian tiff encoding. Default: little + Encoding binary.ByteOrder + + //BigTIFF forces bigtiff creation. Default: false, i.e. only if needed + BigTIFF bool + + // PlanarInterleaving for separate-plane files. + // Default: nil resulting in {{0,1,...n}} i.e. interleaved planes + PlanarInterleaving PlanarInterleaving + + //WithGDALGhostArea inserts gdal specific read optimizations + WithGDALGhostArea bool + + //PreloadTiles is the number of tiles to concurrently preload when writing + PreloadTiles int + + //KeptOverviews if not nil represents the indices of the overviews to keep + KeptOverviews []int + + //KeptMasks if not nil represents the indices of the masks to keep + KeptMasks []int } -func new() *cog { - return &cog{enc: binary.LittleEndian} +func DefaultConfig() Config { + return Config{ + Encoding: binary.LittleEndian, + BigTIFF: false, + WithGDALGhostArea: true, + PreloadTiles: 0, + } +} + +type cog struct { + enc binary.ByteOrder + ifd *IFD + bigtiff bool + withGDALGhost bool + preloadTiles int } func (cog *cog) writeHeader(w io.Writer) error { - glen := uint64(len(ghost)) - if len(cog.ifd.masks) > 0 { - glen = uint64(len(ghostmask)) + glen := uint64(0) + if cog.withGDALGhost { + glen = uint64(len(ghost)) + if cog.ifd.mask != nil { + glen = uint64(len(ghostmask)) + } } var err error if cog.bigtiff { @@ -354,50 +490,14 @@ func (cog *cog) writeHeader(w io.Writer) error { if err != nil { return err } - if len(cog.ifd.masks) > 0 { - _, err = w.Write([]byte(ghostmask)) - } else { - _, err = w.Write([]byte(ghost)) - } - return err -} - -const ( - tByte = 1 - tAscii = 2 - tShort = 3 - tLong = 4 - tRational = 5 - tSByte = 6 - tUndefined = 7 - tSShort = 8 - tSLong = 9 - tSRational = 10 - tFloat = 11 - tDouble = 12 - tLong8 = 16 - tSLong8 = 17 - tIFD8 = 18 -) - -func (cog *cog) computeStructure() { - ifd := cog.ifd - for ifd != nil { - ifd.ntags, ifd.tagsSize, ifd.strileSize, ifd.nplanes = ifd.structure(cog.bigtiff) - //ifd.ntilesx = uint64(math.Ceil(float64(ifd.ImageWidth) / float64(ifd.TileWidth))) - //ifd.ntilesy = uint64(math.Ceil(float64(ifd.ImageLength) / float64(ifd.TileLength))) - ifd.ntilesx = (ifd.ImageWidth + uint64(ifd.TileWidth) - 1) / uint64(ifd.TileWidth) - ifd.ntilesy = (ifd.ImageLength + uint64(ifd.TileLength) - 1) / uint64(ifd.TileLength) - - for _, mifd := range ifd.masks { - mifd.ntags, mifd.tagsSize, mifd.strileSize, mifd.nplanes = mifd.structure(cog.bigtiff) - // mifd.ntilesx = uint64(math.Ceil(float64(mifd.ImageWidth) / float64(mifd.TileWidth))) - // mifd.ntilesy = uint64(math.Ceil(float64(mifd.ImageLength) / float64(mifd.TileLength))) - mifd.ntilesx = (mifd.ImageWidth + uint64(mifd.TileWidth) - 1) / uint64(mifd.TileWidth) - mifd.ntilesy = (mifd.ImageLength + uint64(mifd.TileLength) - 1) / uint64(mifd.TileLength) + if cog.withGDALGhost { + if cog.ifd.mask != nil { + _, err = w.Write([]byte(ghostmask)) + } else { + _, err = w.Write([]byte(ghost)) } - ifd = ifd.overview } + return err } const ghost = `GDAL_STRUCTURAL_METADATA_SIZE=000140 bytes @@ -415,61 +515,61 @@ BLOCK_LEADER=SIZE_AS_UINT4 BLOCK_TRAILER=LAST_4_BYTES_REPEATED KNOWN_INCOMPATIBLE_EDITION=NO MASK_INTERLEAVED_WITH_IMAGERY=YES -` +` //the space at the start of the last line is required to make room for changing NO to YES func (cog *cog) computeImageryOffsets() error { - ifd := cog.ifd - for ifd != nil { - if cog.bigtiff { - ifd.NewTileOffsets64 = make([]uint64, len(ifd.OriginalTileOffsets)) - ifd.NewTileOffsets32 = nil - } else { - ifd.NewTileOffsets32 = make([]uint32, len(ifd.OriginalTileOffsets)) - ifd.NewTileOffsets64 = nil + nplanes := cog.ifd.NPlanes() + haveMask := false + cog.computeStructure(cog.ifd) + if cog.ifd.mask != nil { + cog.computeStructure(cog.ifd.mask) + haveMask = true + } + for _, oifd := range cog.ifd.overviews { + if oifd.NPlanes() != nplanes { + return fmt.Errorf("inconsistent band count") } - //mifd.NewTileOffsets = mifd.OriginalTileOffsets - for _, sc := range ifd.masks { - if cog.bigtiff { - sc.NewTileOffsets64 = make([]uint64, len(sc.OriginalTileOffsets)) - sc.NewTileOffsets32 = nil - } else { - sc.NewTileOffsets32 = make([]uint32, len(sc.OriginalTileOffsets)) - sc.NewTileOffsets64 = nil - } - //sc.NewTileOffsets = sc.OriginalTileOffsets + iHaveMask := oifd.mask != nil + if iHaveMask != haveMask { + return fmt.Errorf("inconsistent mask count") + } + cog.computeStructure(oifd) + if oifd.mask != nil { + cog.computeStructure(oifd.mask) } - ifd = ifd.overview } - cog.computeStructure() //offset to start of image data dataOffset := uint64(16) if !cog.bigtiff { dataOffset = 8 } - if len(cog.ifd.masks) > 0 { - dataOffset += uint64(len(ghostmask)) + 4 - } else { - dataOffset += uint64(len(ghost)) + 4 + if cog.withGDALGhost { + if cog.ifd.mask != nil { + dataOffset += uint64(len(ghostmask) + 4) + } else { + dataOffset += uint64(len(ghost) + 4) + } } - ifd = cog.ifd - for ifd != nil { - dataOffset += ifd.strileSize + ifd.tagsSize - for _, sc := range ifd.masks { - dataOffset += sc.strileSize + sc.tagsSize + dataOffset += uint64(cog.ifd.strileSize + cog.ifd.tagSize) + if cog.ifd.mask != nil { + dataOffset += uint64(cog.ifd.mask.strileSize + cog.ifd.mask.tagSize) + } + for _, ifd := range cog.ifd.overviews { + dataOffset += uint64(ifd.strileSize + ifd.tagSize) + if ifd.mask != nil { + dataOffset += uint64(ifd.mask.strileSize + ifd.mask.tagSize) } - ifd = ifd.overview } - datas := cog.dataInterlacing() - tiles := datas.tiles() + datas := cog.ifdInterlacing() + tiles := cog.tiles(datas, 0) for tile := range tiles { - tileidx := (tile.x+tile.y*tile.ifd.ntilesx)*tile.ifd.nplanes + tile.plane - cnt := uint64(tile.ifd.TileByteCounts[tileidx]) - if cnt > 0 { + tileidx := tile.ifd.TileIdx(tile.x, tile.y, tile.plane) + if tile.ifd.tileLen(tileidx) > 0 { if cog.bigtiff { - tile.ifd.NewTileOffsets64[tileidx] = dataOffset + tile.ifd.newTileOffsets[tileidx] = dataOffset } else { if dataOffset > uint64(^uint32(0)) { //^uint32(0) is max uint32 //rerun with bigtiff support @@ -481,23 +581,68 @@ func (cog *cog) computeImageryOffsets() error { cog.bigtiff = true return cog.computeImageryOffsets() } - tile.ifd.NewTileOffsets32[tileidx] = uint32(dataOffset) + tile.ifd.newTileOffsets[tileidx] = dataOffset } - dataOffset += uint64(tile.ifd.TileByteCounts[tileidx]) + 8 - } else { - if cog.bigtiff { - tile.ifd.NewTileOffsets64[tileidx] = 0 - } else { - tile.ifd.NewTileOffsets32[tileidx] = 0 + dataOffset += tile.ifd.TileByteCounts[tileidx] + if cog.withGDALGhost { + dataOffset += 8 } + } else { + tile.ifd.newTileOffsets[tileidx] = 0 } } - return nil } -func (cog *cog) write(out io.Writer) error { +func (cfg Config) RewriteIFDTree(ifd *IFD, out io.Writer) error { + cog := &cog{ + enc: cfg.Encoding, + bigtiff: cfg.BigTIFF, + withGDALGhost: cfg.WithGDALGhostArea, + preloadTiles: cfg.PreloadTiles, + ifd: ifd, + } + havePlanar := ifd.NPlanes() > 1 + for _, oifd := range ifd.overviews { + if oifd.NPlanes() > 1 { + havePlanar = true + } + } + if havePlanar { + cog.withGDALGhost = false + } + if len(cfg.PlanarInterleaving) == 0 { + //set all unset to default + ifd.setDefaultPlanarInterleaving() + for _, ovr := range ifd.overviews { + ovr.setDefaultPlanarInterleaving() + } + } else { + //set all unset to configured value + if len(ifd.planarInterleaving) == 0 { //don't override existing + if err := ifd.SetPlanarInterleaving(cfg.PlanarInterleaving); err != nil { + return fmt.Errorf("invalid planar interleaving: %w", err) + } + } + for o, ovr := range ifd.overviews { + if len(ovr.planarInterleaving) == 0 { //don't override existing + if err := ovr.SetPlanarInterleaving(cfg.PlanarInterleaving); err != nil { + return fmt.Errorf("invalid planar interleaving for overview %d: %w", o, err) + } + } + } + } + ifd.newTileOffsets = make([]uint64, len(ifd.TileByteCounts)) + if ifd.mask != nil { + ifd.mask.newTileOffsets = make([]uint64, len(ifd.mask.TileByteCounts)) + } + for _, oifd := range ifd.overviews { + oifd.newTileOffsets = make([]uint64, len(oifd.TileByteCounts)) + if oifd.mask != nil { + oifd.mask.newTileOffsets = make([]uint64, len(oifd.mask.TileByteCounts)) + } + } err := cog.computeImageryOffsets() if err != nil { return err @@ -509,47 +654,68 @@ func (cog *cog) write(out io.Writer) error { if !cog.bigtiff { strileData.Offset = 8 } - if len(cog.ifd.masks) > 0 { - strileData.Offset += uint64(len(ghostmask)) - } else { - strileData.Offset += uint64(len(ghost)) + if cog.withGDALGhost { + if ifd.mask != nil { + strileData.Offset += len(ghostmask) + } else { + strileData.Offset += len(ghost) + } } - ifd := cog.ifd - for ifd != nil { - strileData.Offset += ifd.tagsSize - for _, sc := range ifd.masks { - strileData.Offset += sc.tagsSize + strileData.Offset += ifd.tagSize + if ifd.mask != nil { + strileData.Offset += ifd.mask.tagSize + } + for _, oifd := range ifd.overviews { + strileData.Offset += oifd.tagSize + if oifd.mask != nil { + strileData.Offset += oifd.mask.tagSize } - ifd = ifd.overview } - glen := uint64(len(ghost)) - if len(cog.ifd.masks) > 0 { - glen = uint64(len(ghostmask)) + if err := cog.writeHeader(out); err != nil { + return err } - cog.writeHeader(out) - ifd = cog.ifd - off := uint64(16 + glen) + off := 16 if !cog.bigtiff { - off = 8 + glen + off = 8 } - for ifd != nil { - nmasks := len(ifd.masks) - err := cog.writeIFD(out, ifd, off, strileData, nmasks > 0 || ifd.overview != nil) + if cog.withGDALGhost { + if cog.ifd.mask != nil { + off += len(ghostmask) + } else { + off += len(ghost) + } + } + + err = cog.writeIFD(out, ifd, off, strileData, ifd.mask != nil || len(ifd.overviews) > 0) + if err != nil { + return fmt.Errorf("write main ifd: %w", err) + } + off += ifd.tagSize + if ifd.mask != nil { + err = cog.writeIFD(out, ifd.mask, off, strileData, len(ifd.overviews) > 0) if err != nil { - return fmt.Errorf("write ifd: %w", err) + return fmt.Errorf("write mask: %w", err) } - off += ifd.tagsSize - for i, si := range ifd.masks { - err := cog.writeIFD(out, si, off, strileData, i != nmasks-1 || ifd.overview != nil) + off += ifd.mask.tagSize + } + + for i, oifd := range ifd.overviews { + err = cog.writeIFD(out, oifd, off, strileData, + oifd.mask != nil || i != len(ifd.overviews)-1) + if err != nil { + return fmt.Errorf("write overview ifd %d: %w", i, err) + } + off += oifd.tagSize + if oifd.mask != nil { + err := cog.writeIFD(out, oifd.mask, off, strileData, i != len(ifd.overviews)-1) if err != nil { return fmt.Errorf("write ifd: %w", err) } - off += si.tagsSize + off += oifd.mask.tagSize } - ifd = ifd.overview } _, err = out.Write(strileData.Bytes()) @@ -557,28 +723,27 @@ func (cog *cog) write(out io.Writer) error { return fmt.Errorf("write strile pointers: %w", err) } - datas := cog.dataInterlacing() - tiles := datas.tiles() + datas := cog.ifdInterlacing() + tiles := cog.tiles(datas, cog.preloadTiles) data := []byte{} for tile := range tiles { - idx := (tile.x+tile.y*tile.ifd.ntilesx)*tile.ifd.nplanes + tile.plane - bc := tile.ifd.TileByteCounts[idx] + idx := tile.ifd.TileIdx(tile.x, tile.y, tile.plane) + bc := tile.ifd.tileLen(idx) if bc > 0 { - _, err := tile.ifd.r.Seek(int64(tile.ifd.OriginalTileOffsets[idx]), io.SeekStart) - if err != nil { - return fmt.Errorf("seek to %d: %w", tile.ifd.OriginalTileOffsets[idx], err) - } - if uint32(len(data)) < bc+8 { + if len(data) < bc+8 { data = make([]byte, (bc+8)*2) } - binary.LittleEndian.PutUint32(data, bc) //header ghost: tile size - _, err = tile.ifd.r.Read(data[4 : 4+bc]) + binary.LittleEndian.PutUint32(data, uint32(bc)) //header ghost: tile size + err = tile.Data(data[4 : 4+bc]) if err != nil { - return fmt.Errorf("read %d from %d: %w", - bc, tile.ifd.OriginalTileOffsets[idx], err) + return fmt.Errorf("tile.data: %w", err) } copy(data[4+bc:8+bc], data[bc:4+bc]) //trailer ghost: repeat last 4 bytes - _, err = out.Write(data[0 : bc+8]) + if cog.withGDALGhost { + _, err = out.Write(data[0 : bc+8]) + } else { + _, err = out.Write(data[4 : bc+4]) + } if err != nil { return fmt.Errorf("write %d: %w", bc, err) } @@ -588,11 +753,11 @@ func (cog *cog) write(out io.Writer) error { return err } -func (cog *cog) writeIFD(w io.Writer, ifd *ifd, offset uint64, striledata *tagData, next bool) error { +func (cog *cog) writeIFD(w io.Writer, ifd *IFD, offset int, striledata *tagData, next bool) error { - nextOff := uint64(0) + nextOff := 0 if next { - nextOff = offset + ifd.tagsSize + nextOff = offset + ifd.tagSize } var err error // Make space for "pointer area" containing IFD entry data @@ -605,7 +770,7 @@ func (cog *cog) writeIFD(w io.Writer, ifd *ifd, offset uint64, striledata *tagDa } if cog.bigtiff { - err = binary.Write(w, cog.enc, ifd.ntags) + err = binary.Write(w, cog.enc, uint64(ifd.ntags)) } else { err = binary.Write(w, cog.enc, uint16(ifd.ntags)) } @@ -625,8 +790,8 @@ func (cog *cog) writeIFD(w io.Writer, ifd *ifd, offset uint64, striledata *tagDa panic(err) } } - if ifd.ImageLength > 0 { - err := cog.writeField(w, 257, uint32(ifd.ImageLength)) + if ifd.ImageHeight > 0 { + err := cog.writeField(w, 257, uint32(ifd.ImageHeight)) if err != nil { panic(err) } @@ -708,29 +873,29 @@ func (cog *cog) writeIFD(w io.Writer, ifd *ifd, offset uint64, striledata *tagDa } //TileHeight uint16 `tiff:"field,tag=323"` - if ifd.TileLength > 0 { - err := cog.writeField(w, 323, ifd.TileLength) + if ifd.TileHeight > 0 { + err := cog.writeField(w, 323, ifd.TileHeight) if err != nil { panic(err) } } //TileOffsets []uint64 `tiff:"field,tag=324"` - if len(ifd.NewTileOffsets32) > 0 { - err := cog.writeArray(w, 324, ifd.NewTileOffsets32, striledata) - if err != nil { - panic(err) + if len(ifd.newTileOffsets) > 0 { + var err error + if cog.bigtiff { + err = cog.writeArray(w, 324, ifd.newTileOffsets, striledata) + } else { + err = cog.writeArray32(w, 324, ifd.newTileOffsets, striledata) } - } else { - err := cog.writeArray(w, 324, ifd.NewTileOffsets64, striledata) if err != nil { panic(err) } } - //TileByteCounts []uint32 `tiff:"field,tag=325"` + //TileByteCounts []uint64 `tiff:"field,tag=325"` if len(ifd.TileByteCounts) > 0 { - err := cog.writeArray(w, 325, ifd.TileByteCounts, striledata) + err := cog.writeArray32(w, 325, ifd.TileByteCounts, striledata) if err != nil { panic(err) } @@ -835,7 +1000,7 @@ func (cog *cog) writeIFD(w io.Writer, ifd *ifd, offset uint64, striledata *tagDa } if cog.bigtiff { - err = binary.Write(w, cog.enc, nextOff) + err = binary.Write(w, cog.enc, uint64(nextOff)) } else { err = binary.Write(w, cog.enc, uint32(nextOff)) } @@ -850,47 +1015,119 @@ func (cog *cog) writeIFD(w io.Writer, ifd *ifd, offset uint64, striledata *tagDa } type tile struct { - ifd *ifd - x, y uint64 - plane uint64 + ifd *IFD + x, y int + plane int + preloading chan struct{} + preloaded []byte + preloadError error +} + +func (t *tile) Preload() { + t.preloading = make(chan struct{}) + idx := t.ifd.TileIdx(t.x, t.y, t.plane) + tl := t.ifd.tileLen(idx) + t.preloaded = make([]byte, tl) + go func() { + if len(t.preloaded) > 0 { + t.preloadError = t.ifd.LoadTile(idx, t.preloaded) + } + close(t.preloading) + }() } -type datas [][]*ifd +func (t *tile) Data(data []byte) error { + if t.preloading != nil { + <-t.preloading + } + idx := t.ifd.TileIdx(t.x, t.y, t.plane) + { //safety net + tl := t.ifd.tileLen(idx) + if len(data) != int(tl) { + panic("wrong buffer size") + } + } + if t.preloading != nil { + if t.preloadError != nil { + return t.preloadError + } + if len(t.preloaded) != len(data) { + return fmt.Errorf("preloaded buffer len=%d, expecting %d", len(t.preloaded), len(data)) + } + copy(data, t.preloaded) + return nil + } + if len(data) > 0 { + return t.ifd.LoadTile(idx, data) + } + return nil +} -func (cog *cog) dataInterlacing() datas { +type entry struct { //todo: rename this + ifd *IFD + mask *IFD +} + +type entries []entry //todo: rename this + +func (cog *cog) ifdInterlacing() entries { //count overviews - ifdo := cog.ifd - count := 0 - for ifdo != nil { - count++ - ifdo = ifdo.overview - } - ret := make([][]*ifd, count) - ifdo = cog.ifd - for idx := count - 1; idx >= 0; idx-- { - ret[idx] = append(ret[idx], ifdo) - ret[idx] = append(ret[idx], ifdo.masks...) - ifdo = ifdo.overview + ret := make([]entry, 1+len(cog.ifd.overviews)) + havemask := cog.ifd.mask != nil + if havemask { + ret[len(cog.ifd.overviews)] = entry{cog.ifd, cog.ifd.mask} + } else { + ret[len(cog.ifd.overviews)] = entry{cog.ifd, nil} + } + for idx := 0; idx < len(cog.ifd.overviews); idx++ { + oifd := cog.ifd.overviews[len(cog.ifd.overviews)-1-idx] + if havemask { + ret[idx] = entry{oifd, oifd.mask} + } else { + ret[idx] = entry{oifd, nil} + } } return ret } -func (d datas) tiles() chan tile { - ch := make(chan tile) +func (cog *cog) tiles(entries entries, preload int) chan *tile { + ch := make(chan *tile, preload) go func() { defer close(ch) - - for _, ovr := range d { - for y := uint64(0); y < ovr[0].ntilesy; y++ { - for x := uint64(0); x < ovr[0].ntilesx; x++ { - for _, ifd := range ovr { - for p := uint64(0); p < ifd.nplanes; p++ { - ch <- tile{ - ifd: ifd, - plane: p, - x: x, - y: y, + for _, entry := range entries { + maskIdx := -1 + if entry.mask != nil { + if entry.ifd.PlanarConfiguration == 2 { + maskIdx = int(entry.ifd.SamplesPerPixel) + } else { + maskIdx = 1 + } + } + ntx, nty := entry.ifd.NTilesX(), entry.ifd.NTilesY() + for _, l1 := range entry.ifd.planarInterleaving { + for y := 0; y < nty; y++ { + for x := 0; x < ntx; x++ { + for _, p := range l1 { + var tt *tile + if p != maskIdx { + tt = &tile{ + ifd: entry.ifd, + plane: p, + x: x, + y: y, + } + } else { + tt = &tile{ + ifd: entry.mask, + plane: 0, + x: x, + y: y, + } + } + if preload > 0 { + tt.Preload() } + ch <- tt } } } diff --git a/field.go b/field.go index 0b09271..dcd151e 100644 --- a/field.go +++ b/field.go @@ -7,64 +7,87 @@ import ( "math" ) -func arrayFieldSize(data interface{}, bigtiff bool) uint64 { +func arrayFieldSize32(data interface{}, bigtiff bool) int { + ll := 0 + switch d := data.(type) { + case []uint32: + ll = len(d) + case []uint64: + ll = len(d) + default: + panic("bug") + } + if bigtiff { + if ll <= 2 { + return 20 + } + return 20 + 4*ll + } else { + if ll <= 1 { + return 12 + } + return 12 + 4*ll + } +} + +func arrayFieldSize(data interface{}, bigtiff bool) int { if bigtiff { switch d := data.(type) { case []byte: if len(d) <= 8 { return 20 } - return uint64(20 + len(d)) + return 20 + len(d) case []uint16: if len(d) <= 4 { return 20 } - return uint64(20 + 2*len(d)) + return 20 + 2*len(d) case []uint32: if len(d) <= 2 { return 20 } - return uint64(20 + 4*len(d)) + return 20 + 4*len(d) case []uint64: if len(d) <= 1 { return 20 } - return uint64(20 + 8*len(d)) + return 20 + 8*len(d) case []int8: if len(d) <= 8 { return 20 } - return uint64(20 + len(d)) + return 20 + len(d) case []int16: if len(d) <= 4 { return 20 } - return uint64(20 + len(d)*2) + return 20 + len(d)*2 case []int32: if len(d) <= 2 { return 20 } - return uint64(20 + len(d)*4) + return 20 + len(d)*4 case []int64: if len(d) <= 1 { return 20 } - return uint64(20 + len(d)*8) + return 20 + len(d)*8 case []float32: if len(d) <= 2 { return 20 } - return uint64(20 + len(d)*4) + return 20 + len(d)*4 case []float64: if len(d) <= 1 { return 20 } - return uint64(20 + len(d)*8) + return 20 + len(d)*8 case string: if len(d) <= 7 { return 20 } - return uint64(20 + len(d) + 1) + return 20 + len(d) + 1 default: panic("wrong type") } @@ -74,54 +97,67 @@ func arrayFieldSize(data interface{}, bigtiff bool) uint64 { if len(d) <= 4 { return 12 } - return uint64(12 + len(d)) + return 12 + len(d) case []uint16: if len(d) <= 2 { return 12 } - return uint64(12 + 2*len(d)) + return 12 + 2*len(d) case []uint32: if len(d) <= 1 { return 12 } - return uint64(12 + 4*len(d)) + return 12 + 4*len(d) case []int8: if len(d) <= 4 { return 12 } - return uint64(12 + len(d)) + return 12 + len(d) case []int16: if len(d) <= 2 { return 12 } - return uint64(12 + len(d)*2) + return 12 + len(d)*2 case []int32: if len(d) <= 1 { return 12 } - return uint64(12 + len(d)*4) + return 12 + len(d)*4 case []float32: if len(d) <= 1 { return 12 } - return uint64(12 + len(d)*4) + return 12 + len(d)*4 case string: if len(d) <= 3 { return 12 } - return uint64(12 + len(d) + 1) + return 12 + len(d) + 1 case []float64: - return uint64(12 + len(d)*8) + return 12 + len(d)*8 case []int64: - return uint64(12 + len(d)*8) + return 12 + len(d)*8 case []uint64: - return uint64(12 + len(d)*8) + return 12 + len(d)*8 default: panic("wrong type") } } } +func (cog *cog) writeArray32(w io.Writer, tag uint16, data interface{}, tags *tagData) error { + switch d := data.(type) { + case []uint64: + d32 := make([]uint32, len(d)) + for i := range d { + d32[i] = uint32(d[i]) + } + return cog.writeArray(w, tag, d32, tags) + default: + panic("bug") + } +} + func (cog *cog) writeArray(w io.Writer, tag uint16, data interface{}, tags *tagData) error { var buf []byte if cog.bigtiff { @@ -141,7 +177,7 @@ func (cog *cog) writeArray(w io.Writer, tag uint16, data interface{}, tags *tagD buf[12+i] = d[i] } } else { - cog.enc.PutUint64(buf[12:], tags.NextOffset()) + cog.enc.PutUint64(buf[12:], uint64(tags.NextOffset())) tags.Write(d) } } else { @@ -165,9 +201,11 @@ func (cog *cog) writeArray(w io.Writer, tag uint16, data interface{}, tags *tagD cog.enc.PutUint16(buf[12+i*2:], d[i]) } } else { - cog.enc.PutUint64(buf[12:], tags.NextOffset()) + cog.enc.PutUint64(buf[12:], uint64(tags.NextOffset())) for i := 0; i < n; i++ { - binary.Write(tags, cog.enc, d[i]) + if err := binary.Write(tags, cog.enc, d[i]); err != nil { + return err + } } } } else { @@ -179,7 +217,9 @@ func (cog *cog) writeArray(w io.Writer, tag uint16, data interface{}, tags *tagD } else { cog.enc.PutUint32(buf[8:], uint32(tags.NextOffset())) for i := 0; i < n; i++ { - binary.Write(tags, cog.enc, d[i]) + if err := binary.Write(tags, cog.enc, d[i]); err != nil { + return err + } } } } @@ -193,9 +233,11 @@ func (cog *cog) writeArray(w io.Writer, tag uint16, data interface{}, tags *tagD cog.enc.PutUint32(buf[12+i*4:], d[i]) } } else { - cog.enc.PutUint64(buf[12:], tags.NextOffset()) + cog.enc.PutUint64(buf[12:], uint64(tags.NextOffset())) for i := 0; i < n; i++ { - binary.Write(tags, cog.enc, d[i]) + if err := binary.Write(tags, cog.enc, d[i]); err != nil { + return err + } } } } else { @@ -207,7 +249,9 @@ func (cog *cog) writeArray(w io.Writer, tag uint16, data interface{}, tags *tagD } else { cog.enc.PutUint32(buf[8:], uint32(tags.NextOffset())) for i := 0; i < n; i++ { - binary.Write(tags, cog.enc, d[i]) + if err := binary.Write(tags, cog.enc, d[i]); err != nil { + return err + } } } } @@ -219,16 +263,20 @@ func (cog *cog) writeArray(w io.Writer, tag uint16, data interface{}, tags *tagD if n <= 1 { cog.enc.PutUint64(buf[12:], d[0]) } else { - cog.enc.PutUint64(buf[12:], tags.NextOffset()) + cog.enc.PutUint64(buf[12:], uint64(tags.NextOffset())) for i := 0; i < n; i++ { - binary.Write(tags, cog.enc, d[i]) + if err := binary.Write(tags, cog.enc, d[i]); err != nil { + return err + } } } } else { cog.enc.PutUint32(buf[4:8], uint32(n)) cog.enc.PutUint32(buf[8:], uint32(tags.NextOffset())) for i := 0; i < n; i++ { - binary.Write(tags, cog.enc, d[i]) + if err := binary.Write(tags, cog.enc, d[i]); err != nil { + return err + } } } case []float32: @@ -241,9 +289,11 @@ func (cog *cog) writeArray(w io.Writer, tag uint16, data interface{}, tags *tagD cog.enc.PutUint32(buf[12+i*4:], math.Float32bits(d[i])) } } else { - cog.enc.PutUint64(buf[12:], tags.NextOffset()) + cog.enc.PutUint64(buf[12:], uint64(tags.NextOffset())) for i := 0; i < n; i++ { - binary.Write(tags, cog.enc, math.Float32bits(d[i])) + if err := binary.Write(tags, cog.enc, math.Float32bits(d[i])); err != nil { + return err + } } } } else { @@ -255,7 +305,9 @@ func (cog *cog) writeArray(w io.Writer, tag uint16, data interface{}, tags *tagD } else { cog.enc.PutUint32(buf[8:], uint32(tags.NextOffset())) for i := 0; i < n; i++ { - binary.Write(tags, cog.enc, math.Float32bits(d[i])) + if err := binary.Write(tags, cog.enc, math.Float32bits(d[i])); err != nil { + return err + } } } } @@ -269,16 +321,20 @@ func (cog *cog) writeArray(w io.Writer, tag uint16, data interface{}, tags *tagD cog.enc.PutUint64(buf[12+i*4:], math.Float64bits(d[0])) } } else { - cog.enc.PutUint64(buf[12:], tags.NextOffset()) + cog.enc.PutUint64(buf[12:], uint64(tags.NextOffset())) for i := 0; i < n; i++ { - binary.Write(tags, cog.enc, math.Float64bits(d[i])) + if err := binary.Write(tags, cog.enc, math.Float64bits(d[i])); err != nil { + return err + } } } } else { cog.enc.PutUint32(buf[4:8], uint32(n)) cog.enc.PutUint32(buf[8:], uint32(tags.NextOffset())) for i := 0; i < n; i++ { - binary.Write(tags, cog.enc, math.Float64bits(d[i])) + if err := binary.Write(tags, cog.enc, math.Float64bits(d[i])); err != nil { + return err + } } } case string: @@ -292,7 +348,7 @@ func (cog *cog) writeArray(w io.Writer, tag uint16, data interface{}, tags *tagD } buf[12+n-1] = 0 } else { - cog.enc.PutUint64(buf[12:], tags.NextOffset()) + cog.enc.PutUint64(buf[12:], uint64(tags.NextOffset())) tags.Write(append([]byte(d), 0)) } } else { diff --git a/go.mod b/go.mod index 8640303..347c5e2 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,77 @@ module github.com/airbusgeo/cogger -go 1.16 +go 1.21 -require github.com/google/tiff v0.0.0-20161109161721-4b31f3041d9a +require ( + cloud.google.com/go/storage v1.20.0 + github.com/airbusgeo/godal v0.0.6 + github.com/airbusgeo/osio v0.1.3 + github.com/argoproj/argo-workflows/v3 v3.3.9 + github.com/google/tiff v0.0.0-20161109161721-4b31f3041d9a + github.com/google/uuid v1.3.0 + github.com/spf13/cobra v1.8.1 + github.com/stretchr/testify v1.8.0 + github.com/tbonfort/gobs v0.0.1 + go.airbusds-geo.com/gcp v0.0.8 + go.airbusds-geo.com/log v1.3.0 + k8s.io/api v0.24.3 + k8s.io/apimachinery v0.24.3 + sigs.k8s.io/yaml v1.3.0 +) + +require ( + cloud.google.com/go v0.100.2 // indirect + cloud.google.com/go/compute v1.3.0 // indirect + cloud.google.com/go/iam v0.1.1 // indirect + github.com/PuerkitoBio/purell v1.1.1 // indirect + github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect + github.com/airbusgeo/errs v0.0.2 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/emicklei/go-restful/v3 v3.8.0 // indirect + github.com/go-logr/logr v1.2.2 // indirect + github.com/go-openapi/jsonpointer v0.19.5 // indirect + github.com/go-openapi/jsonreference v0.19.6 // indirect + github.com/go-openapi/swag v0.19.15 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/google/gnostic v0.5.7-v3refs // indirect + github.com/google/go-cmp v0.5.7 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/googleapis/gax-go/v2 v2.1.1 // indirect + github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect + github.com/hashicorp/golang-lru v0.5.4 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + go.opencensus.io v0.23.0 // indirect + go.uber.org/atomic v1.7.0 // indirect + go.uber.org/multierr v1.6.0 // indirect + go.uber.org/zap v1.17.0 // indirect + golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect + golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect + golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 // indirect + golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect + golang.org/x/text v0.3.7 // indirect + golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + google.golang.org/api v0.70.0 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c // indirect + google.golang.org/grpc v1.44.0 // indirect + google.golang.org/protobuf v1.27.1 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/client-go v0.24.3 // indirect + k8s.io/klog/v2 v2.60.1 // indirect + k8s.io/kube-openapi v0.0.0-20220627174259-011e075b9cb8 // indirect + k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 // indirect + sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect +) diff --git a/go.sum b/go.sum index bb6c691..ce16607 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,932 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= +cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= +cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= +cloud.google.com/go v0.88.0/go.mod h1:dnKwfYbP9hQhefiUvpbcAyoGSHUrOxR20JVElLiUvEY= +cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= +cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= +cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= +cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= +cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= +cloud.google.com/go v0.100.1/go.mod h1:fs4QogzfH5n2pBXBP9vRiU+eCny7lD2vmFZy79Iuw1U= +cloud.google.com/go v0.100.2 h1:t9Iw5QH5v4XtlEQaCtUY7x6sCABps8sW0acw7e2WQ6Y= +cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= +cloud.google.com/go/compute v1.3.0 h1:mPL/MzDDYHsh5tHRS9mhmhWlcgClCrCa6ApQCU6wnHI= +cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/iam v0.1.1 h1:4CapQyNFjiksks1/x7jsvsygFPhihslYk5GptIrlX68= +cloud.google.com/go/iam v0.1.1/go.mod h1:CKqrcnI/suGpybEHxZ7BMehL0oA4LpdyJdUlTl9jVMw= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/pubsub v1.4.0/go.mod h1:LFrqilwgdw4X2cJS9ALgzYmMu+ULyrUN6IHV3CPK4TM= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/storage v1.15.0/go.mod h1:mjjQMoxxyGH7Jr8K5qrx6N2O0AHsczI61sMNn03GIZI= +cloud.google.com/go/storage v1.17.0/go.mod h1:0wRtHSM3Npk/QJYdwcpRNVRVJlH2OxyWF9Dws3J+MtE= +cloud.google.com/go/storage v1.20.0 h1:kv3rQ3clEQdxqokkCCgQo+bxPqcuXiROjxvnKb8Oqdk= +cloud.google.com/go/storage v1.20.0/go.mod h1:TiC1o6FxNCG8y5gB7rqCsFZCIYPMPZCO81ppOoEPLGI= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= +github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= +github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= +github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +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/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/airbusgeo/cogger v0.0.7/go.mod h1:kCWuN4IVKpUiLzGKrqsZHPYbN24nyyrRuAa/vX2eURk= +github.com/airbusgeo/errs v0.0.2 h1:+7i9HLCgs8xWkGN7cIUMStVUA1d6F5AqpAlft6O4kM8= +github.com/airbusgeo/errs v0.0.2/go.mod h1:w6O7kns8m5o+eQeWsbhU7usUNq2Rhmt4xQO+rVt9/Js= +github.com/airbusgeo/godal v0.0.6 h1:o+9clgaSsNJTJa9kKEUU57K9aPadqBYHPgbu0q6/dyE= +github.com/airbusgeo/godal v0.0.6/go.mod h1:9YLEeqXT8XI39o/l3lVjK8sCcEwu3Nt0RkP88J1jHkg= +github.com/airbusgeo/osio v0.1.2/go.mod h1:d4qlaDSgsMilFoi+/62vwFZStuYIHIDovYzIRjTK+Jw= +github.com/airbusgeo/osio v0.1.3 h1:IxNf1co8lxwqJCziV+5qZyZbkT6JYdmR3L0oomNffrk= +github.com/airbusgeo/osio v0.1.3/go.mod h1:d4qlaDSgsMilFoi+/62vwFZStuYIHIDovYzIRjTK+Jw= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/argoproj/argo-workflows/v3 v3.3.9 h1:4UobY68Dh+guXSVUwchgr53n9nvHZl3bmZlfehcJLeo= +github.com/argoproj/argo-workflows/v3 v3.3.9/go.mod h1:Cg442YnzaUxILjmk6xMZo19X87Feev1DyEX4Onj08vo= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/aws/aws-sdk-go-v2 v1.7.0/go.mod h1:tb9wi5s61kTDA5qCkcDbt3KRVV74GGslQkl/DRdX/P4= +github.com/aws/aws-sdk-go-v2/config v1.4.0/go.mod h1:lSD+PE8OsriBSidyfYyAadDrbJrUJTlBd3IF0qXkszQ= +github.com/aws/aws-sdk-go-v2/credentials v1.3.0/go.mod h1:tOcv+qDZ0O+6Jk2beMl5JnZX6N0H7O8fw9UsD3bP7GI= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.2.0/go.mod h1:XvzoGzuS0kKPzCQtJCC22Xh/mMgVAzfGo/0V+mk/Cu0= +github.com/aws/aws-sdk-go-v2/internal/ini v1.0.1/go.mod h1:qGQ/9IfkZonRNSNLE99/yBJ7EPA/h8jlWEqtJCcaj+Q= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.2.0/go.mod h1:2Kc2Pybp1Hr2ZCCOz78mWnNSZYEKKBQgNcizVGk9sko= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.2.0/go.mod h1:a7XLWNKuVgOxjssEF019IiHPv35k8KHBaWv/wJAfi2A= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.5.0/go.mod h1:541bxEA+Z8quwit9ZT7uxv/l9xRz85/HS41l9OxOQdY= +github.com/aws/aws-sdk-go-v2/service/s3 v1.11.0/go.mod h1:zJe8mEFDS2F04nO0pKVBPfArAv2ycC6wt3ILvrV4SQw= +github.com/aws/aws-sdk-go-v2/service/sso v1.3.0/go.mod h1:qWR+TUuvfji9udM79e4CPe87C5+SjMEb2TFXkZaI0Vc= +github.com/aws/aws-sdk-go-v2/service/sts v1.5.0/go.mod h1:HjDKUmissf6Mlut+WzG2r35r6LeTKmLEDJ6p9NryzLg= +github.com/aws/smithy-go v1.5.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +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= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful/v3 v3.8.0 h1:eCZ8ulSerjdAiaNpF7GxXIE7ZCMo1moN1qX+S609eVw= +github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.2 h1:ahHml/yUpnlb96Rp8HCvtYVPY8ZYpxq3g7UYchIYwbs= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= +github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs= +github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= +github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= +github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= +github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.2.1 h1:d8MncMlErDFTwQGBK1xhv026j9kqhvw1Qv9IbWT1VLQ= +github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210715191844-86eeefc3e471/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/tiff v0.0.0-20161109161721-4b31f3041d9a h1:Mi5lnNcpqYN2M2J52gy1uknSRhfA9BBAPVBd5XfHi+U= github.com/google/tiff v0.0.0-20161109161721-4b31f3041d9a/go.mod h1:gpYY+jaYz1cbbiPKT9p2ReLdpBTvTRqoKwJ21LdEk+4= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= +github.com/googleapis/gax-go/v2 v2.1.1 h1:dp3bWCh+PPO1zjRRiCSczJav13sBvG4UhNyVTa1KqdU= +github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +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/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= +github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +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/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/tbonfort/gobs v0.0.1 h1:fhAiwb5ZNv/DkjFoa8vy8+rV/FzGWkb5IkZuNq8D/zo= +github.com/tbonfort/gobs v0.0.1/go.mod h1:JwSDM2Af8aAf0N1r31+7aCo40BFMXaPUPs0htjs2i94= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.airbusds-geo.com/gcp v0.0.8 h1:KVNJNP6Qi9CeQSiopioixNTRy8iqEUYLlFRRPNOsXWA= +go.airbusds-geo.com/gcp v0.0.8/go.mod h1:gRyoGlNBdJJMq2fdttNGt6x0DDEEa2PI6FbCS01TxgY= +go.airbusds-geo.com/log v1.1.0/go.mod h1:juceZExZSrRB/w6YwhmyLtuzjoapcOkGvIO930WqQMo= +go.airbusds-geo.com/log v1.3.0 h1:sy62pHn94tpKOH4qHn0UD9UdblYXHdvxiEmKIRJoN6w= +go.airbusds-geo.com/log v1.3.0/go.mod h1:rS2C/bMiK1ksJOz3c33NHfBPKO2QFFMlN0oHZrKrSLk= +go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= +go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= +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.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= +go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +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-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/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/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +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= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +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/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.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +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-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= +golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210413134643-5e61552d6c78/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +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-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210412220455-f1c623a9e750/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 h1:OH54vjqzRWmbJ62fjuhxy7AxFFgoHN0/DPc/UrL8cAs= +golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= +golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +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-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/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-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/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-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/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.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200527183253-8e7acdbce89d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +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 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.25.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= +google.golang.org/api v0.45.0/go.mod h1:ISLIJCedJolbZvDfAk+Ctuq5hf+aJ33WgtUsfyFoLXA= +google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= +google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= +google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= +google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= +google.golang.org/api v0.52.0/go.mod h1:Him/adpjt0sxtkWViy0b6xyKW/SD71CwdJ7HqJo7SrU= +google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= +google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= +google.golang.org/api v0.58.0/go.mod h1:cAbP2FsxoGVNwtgNAmmn3y5G1TWAiVYRmg4yku3lv+E= +google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= +google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= +google.golang.org/api v0.64.0/go.mod h1:931CdxA8Rm4t6zqTFGSsgwbAEZ2+GMYurbndwSimebM= +google.golang.org/api v0.66.0/go.mod h1:I1dmXYpX7HGwz/ejRxwQp2qj5bFAz93HiCU1C1oYd9M= +google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= +google.golang.org/api v0.70.0 h1:67zQnAE0T2rB0A3CwLSas0K+SbVzSxP+zTLkQLexeiw= +google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200528110217-3d3490e7e671/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210413151531-c14fb6ef47c3/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/genproto v0.0.0-20210420162539-3c870d7478d2/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= +google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210721163202-f1cecdd8b78a/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= +google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210917145530-b395a37504d4/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210921142501-181ce0d877f6/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211223182754-3ac035c7e7cb/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220111164026-67b88f271998/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220114231437-d2e6a121cae0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220201184016-50beb8ab5c44/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c h1:TU4rFa5APdKTq0s6B7WTsH6Xmx0Knj86s6Biz56mErE= +google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.44.0 h1:weqSxi/TMs1SqFRMHCtBgXRs8k3X39QIDEZ0pRcttUg= +google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +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/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +k8s.io/api v0.24.3 h1:tt55QEmKd6L2k5DP6G/ZzdMQKvG5ro4H4teClqm0sTY= +k8s.io/api v0.24.3/go.mod h1:elGR/XSZrS7z7cSZPzVWaycpJuGIw57j9b95/1PdJNI= +k8s.io/apimachinery v0.24.3 h1:hrFiNSA2cBZqllakVYyH/VyEh4B581bQRmqATJSeQTg= +k8s.io/apimachinery v0.24.3/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= +k8s.io/client-go v0.24.3 h1:Nl1840+6p4JqkFWEW2LnMKU667BUxw03REfLAVhuKQY= +k8s.io/client-go v0.24.3/go.mod h1:AAovolf5Z9bY1wIg2FZ8LPQlEdKHjLI7ZD4rw920BJw= +k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= +k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= +k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= +k8s.io/klog/v2 v2.60.1 h1:VW25q3bZx9uE3vvdL6M8ezOX79vA2Aq1nEWLqNQclHc= +k8s.io/klog/v2 v2.60.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42/go.mod h1:Z/45zLw8lUo4wdiUkI+v/ImEGAvu3WatcZl3lPMR4Rk= +k8s.io/kube-openapi v0.0.0-20220627174259-011e075b9cb8 h1:yEQKdMCjzAOvGeiTwG4hO/hNVNtDOuUFvMUZ0OlaIzs= +k8s.io/kube-openapi v0.0.0-20220627174259-011e075b9cb8/go.mod h1:mbJ+NSUoAhuR14N0S63bPkh8MGVSo3VYSGZtH/mfMe0= +k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 h1:HNSDgDCrr/6Ly3WEGKZftiE7IY19Vz2GdbOCyI4qqhc= +k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 h1:kDi4JBNAsJWfz1aEXhO8Jg87JJaPNLh5tIzYHgStQ9Y= +sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY= +sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/structured-merge-diff/v4 v4.2.1 h1:bKCqE9GvQ5tiVHn5rfn1r+yao3aLQEaLzkkmAkf+A6Y= +sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/loader.go b/loader.go index 8a9c06d..1a7ea9e 100644 --- a/loader.go +++ b/loader.go @@ -3,28 +3,30 @@ package cogger import ( "fmt" "io" + "slices" "sort" "github.com/google/tiff" ) -func loadMultipleTIFFs(tifs []tiff.TIFF) ([]*ifd, error) { - ifds := make([]*ifd, 0) - for it, tif := range tifs { +func loadTIFFs(tifRs []tiff.ReadAtReadSeeker) ([]*IFD, error) { + var order string + ifds := make([]*IFD, 0) + for ii, tifr := range tifRs { + tif, err := tiff.Parse(tifr, nil, nil) + if err != nil { + return nil, fmt.Errorf("parse tiff %d: %w", ii, err) + } + if ii == 0 { + order = tif.Order() + } else if tif.Order() != order { + return nil, fmt.Errorf("inconsistent tif byte ordering") + } tifds := tif.IFDs() for i := range tifds { ifd, err := loadIFD(tif.R(), tifds[i]) if err != nil { - return nil, err - } - if it != 0 { - //check that the additional files are smaller than the first, i.e. that they represent an overview - if ifd.ImageLength >= ifds[0].ImageLength || ifd.ImageWidth >= ifds[0].ImageWidth { - return nil, fmt.Errorf("provided tiff %d size %dx%d is larger than first tiff size %dx%d. when using multiple files, the subsequent ones must be overviews of the first one", - it, ifd.ImageWidth, ifd.ImageLength, ifds[0].ImageWidth, ifds[0].ImageLength) - } - //force to overview - ifd.SubfileType |= subfileTypeReducedImage + return nil, fmt.Errorf("load ifd %d of tif %d: %w", i, ii, err) } ifds = append(ifds, ifd) } @@ -32,134 +34,82 @@ func loadMultipleTIFFs(tifs []tiff.TIFF) ([]*ifd, error) { return ifds, nil } -func loadSingleTIFF(tif tiff.TIFF) ([]*ifd, error) { - tifds := tif.IFDs() - ifds := make([]*ifd, len(tifds)) - var err error - for i := range tifds { - ifds[i], err = loadIFD(tif.R(), tifds[i]) - if err != nil { - return nil, err - } - } - return ifds, nil -} - -func loadIFD(r tiff.BReader, tifd tiff.IFD) (*ifd, error) { - ifd := &ifd{r: r} +func loadIFD(r tiff.ReadAtReadSeeker, tifd tiff.IFD) (*IFD, error) { + ifd := &IFD{} err := tiff.UnmarshalIFD(tifd, ifd) if err != nil { return nil, err } - if len(ifd.TempTileByteCounts) > 0 { - ifd.TileByteCounts = make([]uint32, len(ifd.TempTileByteCounts)) - for i := range ifd.TempTileByteCounts { - ifd.TileByteCounts[i] = uint32(ifd.TempTileByteCounts[i]) + if len(ifd.TileByteCounts) == 0 || len(ifd.TileByteCounts) != len(ifd.TileOffsets) { + return nil, fmt.Errorf("ifd is not tiled") + } + ifd.LoadTile = func(idx int, data []byte) error { + if idx >= len(ifd.TileByteCounts) || len(data) != int(ifd.TileByteCounts[idx]) { + panic("bug") } - ifd.TempTileByteCounts = nil //reclaim mem + _, err = r.ReadAt(data, int64(ifd.TileOffsets[idx])) + return err } return ifd, nil } // Rewrite reshuffles the tiff bytes provided as readers into a COG output // to out +// +// Deprecated: use Config.Rewrite in order to use non-default options func Rewrite(out io.Writer, readers ...tiff.ReadAtReadSeeker) error { - tiffs := []tiff.TIFF{} + return DefaultConfig().Rewrite(out, readers...) +} + +func (cfg Config) Rewrite(out io.Writer, readers ...tiff.ReadAtReadSeeker) error { if len(readers) == 0 { return fmt.Errorf("missing readers") } - for i, r := range readers { - tif, err := tiff.Parse(r, nil, nil) - if err != nil { - return fmt.Errorf("parse tiff %d: %w", i, err) - } - tiffs = append(tiffs, tif) - } - err := sanityCheck(tiffs) + ifds, err := loadTIFFs(readers) if err != nil { - return fmt.Errorf("consistency check: %w", err) - } - var ifds []*ifd - if len(tiffs) > 1 { - ifds, err = loadMultipleTIFFs(tiffs) - if err != nil { - return fmt.Errorf("load: %w", err) - } - } else { - ifds, err = loadSingleTIFF(tiffs[0]) - if err != nil { - return fmt.Errorf("load: %w", err) - } + return fmt.Errorf("load: %w", err) } sort.Slice(ifds, func(i, j int) bool { //return in order: fullres, fullresmasks, ovr1, ovr1masks, ovr2, .... - if ifds[i].ImageLength != ifds[j].ImageLength { - return ifds[i].ImageLength > ifds[j].ImageLength + if ifds[i].ImageHeight != ifds[j].ImageHeight { + return ifds[i].ImageHeight > ifds[j].ImageHeight } return ifds[i].SubfileType < ifds[j].SubfileType }) if ifds[0].SubfileType != 0 { - return fmt.Errorf("failed sort: first px=%d type=%d", ifds[0].ImageLength, ifds[0].SubfileType) + return fmt.Errorf("failed sort: first px=%d type=%d", ifds[0].ImageHeight, ifds[0].SubfileType) } - cog := new() - cog.ifd = ifds[0] - curOvr := cog.ifd - l := curOvr.ImageLength + curOvr := ifds[0] + l := curOvr.ImageHeight + ovrIndex := -1 + maskIndex := -1 for _, ci := range ifds[1:] { - if ci.ImageLength == l { - err = curOvr.AddMask(ci) - if err != nil { - return err + err = nil + if ci.ImageHeight == l { + maskIndex++ + if curOvr != nil && (cfg.KeptMasks == nil || slices.Contains(cfg.KeptMasks, maskIndex)) { + err = curOvr.AddMask(ci) } } else { - curOvr.AddOverview(ci) - curOvr = ci - l = curOvr.ImageLength + ovrIndex++ + maskIndex = -1 + l = ci.ImageHeight + if cfg.KeptOverviews == nil || slices.Contains(cfg.KeptOverviews, ovrIndex) { + curOvr = ci + err = ifds[0].AddOverview(ci) + } else { + curOvr = nil + } + } + if err != nil { + return fmt.Errorf("failed to add overview/mask %dx%dx%d: %w", + ci.ImageWidth, ci.ImageHeight, ci.SamplesPerPixel, err) } } - err = cog.write(out) + err = cfg.RewriteIFDTree(ifds[0], out) if err != nil { return fmt.Errorf("mucog write: %w", err) } return nil } - -func sanityCheck(tiffs []tiff.TIFF) error { - if len(tiffs) == 0 { - return fmt.Errorf("no tiffs") - } - order := tiffs[0].Order() - if order != "MM" && order != "II" { - return fmt.Errorf("unknown byte order") - } - for it, tif := range tiffs { - if tif.Order() != order { - return fmt.Errorf("inconsistent byte order") - } - for ii, ifd := range tif.IFDs() { - err := sanityCheckIFD(ifd) - if err != nil { - return fmt.Errorf("tif %d ifd %d: %w", it, ii, err) - } - } - } - return nil -} - -func sanityCheckIFD(ifd tiff.IFD) error { - to := ifd.GetField(324) - tl := ifd.GetField(325) - if to == nil || tl == nil { - return fmt.Errorf("no tiles") - } - if to.Count() != tl.Count() { - return fmt.Errorf("inconsistent tile off/len count") - } - so := ifd.GetField(272) - sl := ifd.GetField(279) - if so != nil || sl != nil { - return fmt.Errorf("tif has strips") - } - return nil -} diff --git a/testdata/cog_band4.tif b/testdata/cog_band4.tif index 46ccb2a..017b9c7 100644 Binary files a/testdata/cog_band4.tif and b/testdata/cog_band4.tif differ diff --git a/testdata/cog_band4mask.tif b/testdata/cog_band4mask.tif index 9d4c443..d5ee7c0 100644 Binary files a/testdata/cog_band4mask.tif and b/testdata/cog_band4mask.tif differ diff --git a/tiler.go b/tiler.go new file mode 100644 index 0000000..0c02ee7 --- /dev/null +++ b/tiler.go @@ -0,0 +1,472 @@ +package cogger + +import ( + "fmt" + "io" + "math" + + "github.com/airbusgeo/osio" + "github.com/google/tiff" +) + +type Tiler struct { + targetTilePixelCount int + minOverviewSize int + internalTilingWidth, internalTilingHeight int + overviewCount int + width, height int + pyr Pyramid + preloadTiles int +} + +type ErrInvalidOption struct { + msg string +} + +func (err ErrInvalidOption) Error() string { + return err.msg +} + +type TilerOption func(t *Tiler) error + +func InternalTileSize(width, height int) TilerOption { + return func(t *Tiler) error { + if width <= 0 || height <= 0 { + return ErrInvalidOption{"internal tile width and height must be >=1"} + } + t.internalTilingWidth, t.internalTilingHeight = width, height + return nil + } +} + +func MinOverviewSize(size int) TilerOption { + return func(t *Tiler) error { + if size <= 0 { + return ErrInvalidOption{"minimal overview size must be >=1"} + } + t.minOverviewSize = size + return nil + } +} + +func OverviewCount(count int) TilerOption { + return func(t *Tiler) error { + if count < 0 { + return ErrInvalidOption{"overview count must be >=0"} + } + t.overviewCount = count + return nil + } +} +func TargetPixelCount(count int) TilerOption { + return func(t *Tiler) error { + if count < 0 { + return ErrInvalidOption{"target pixel count must be >=0"} + } + t.targetTilePixelCount = count + return nil + } +} +func PreloadTiles(count int) TilerOption { + return func(t *Tiler) error { + if count < 0 { + return ErrInvalidOption{"preloaded tiles must be >=0"} + } + t.preloadTiles = count + return nil + } +} + +func NewTiler(width, height int, options ...TilerOption) (Tiler, error) { + var err error + t := Tiler{ + width: width, + height: height, + targetTilePixelCount: 8192 * 8192, + internalTilingWidth: 256, + internalTilingHeight: 256, + overviewCount: -1, + minOverviewSize: 2, + preloadTiles: 0, + } + for _, o := range options { + if err := o(&t); err != nil { + return t, err + } + } + if t.pyr, err = t.pyramid(width, height); err != nil { + return t, err + } + return t, nil +} + +type Strip struct { + SrcTopLeftX, SrcTopLeftY float64 + SrcBottomRightX, SrcBottomRightY float64 + SrcWidth, SrcHeight float64 + TargetWidth, TargetHeight int +} + +type Image struct { + internalTilingWidth, internalTilingHeight int + Width, Height int + Strips []Strip +} + +type Pyramid []Image + +func (t Tiler) Tiling() Pyramid { + return t.pyr +} + +type DagStrip struct { + Level, Strip int //index inside pyramid + Parents []DagStrip //max 3 +} + +func (t Tiler) DAG(pyr Pyramid) (DagStrip, error) { + if len(pyr[len(pyr)-1].Strips) != 1 { + return DagStrip{}, fmt.Errorf("BUG: lowest resolution has more than 1 strip") + } + last := DagStrip{ + Level: len(pyr) - 1, + Strip: 0, + } + + var parents func(level, strip int) []DagStrip + parents = func(level, strip int) []DagStrip { + if level == 0 { + panic("bug") + } + curstrip := pyr[level].Strips[strip] + top := math.Floor(curstrip.SrcTopLeftX) + bottom := math.Ceil(curstrip.SrcBottomRightX) + + h := 0.0 + var ret []DagStrip + for psi, ps := range pyr[level-1].Strips { + if top > h+ps.SrcHeight || bottom < h { + continue + } + parent := DagStrip{ + Level: level - 1, + Strip: psi, + } + if parent.Level > 0 { + parent.Parents = parents(parent.Level, parent.Strip) + } + ret = append(ret, parent) + + h += curstrip.SrcHeight + } + return ret + } + + if last.Level > 0 { + last.Parents = parents(last.Level, last.Strip) + } + return last, nil +} + +func (t Tiler) pyramid(width, height int) (Pyramid, error) { + if width*height == 0 { + return nil, ErrInvalidOption{"cannot tile 0-sized image"} + } + overviewCount := t.overviewCount + if overviewCount == -1 { + iw, ih := width, height + overviewCount = 0 + for (iw > t.internalTilingWidth || ih > t.internalTilingHeight) && + (iw > t.minOverviewSize && ih > t.minOverviewSize) { + overviewCount++ + iw /= 2 + ih /= 2 + } + } + pyramid := make([]Image, overviewCount+1) + + iw, ih := width, height + pyramid[0] = t.tiling(width, height, width, height) + for ovr := 1; ovr <= overviewCount; ovr++ { + if (iw/2)*(ih/2) == 0 { + return nil, ErrInvalidOption{"requested overview count results in 0-sized image"} + } + pyramid[ovr] = t.tiling(iw, ih, iw/2, ih/2) + iw /= 2 + ih /= 2 + } + return pyramid, nil +} + +func (t Tiler) tiling(srcWidth, srcHeight, dstWidth, dstHeight int) Image { + if dstWidth*dstHeight == 0 { + panic("0 sized tiling") + } + numStrips := (dstWidth * dstHeight) / t.targetTilePixelCount + if numStrips == 0 { + numStrips = 1 + } + stripHeight := dstHeight / numStrips + if stripHeight <= t.internalTilingHeight { + stripHeight = t.internalTilingHeight + } + if stripHeight%t.internalTilingHeight != 0 { + stripHeight = (stripHeight/t.internalTilingHeight + 1) * t.internalTilingHeight + } + + resY := float64(srcHeight) / float64(dstHeight) + img := Image{ + internalTilingWidth: t.internalTilingHeight, + internalTilingHeight: t.internalTilingHeight, + Width: dstWidth, + Height: dstHeight, + } + dstRow := 0 + srcRow := float64(0) + for dstRow < dstHeight { + thisHeight := stripHeight + if dstRow+stripHeight > dstHeight { + thisHeight = dstHeight - dstRow + } + img.Strips = append(img.Strips, Strip{ + SrcTopLeftX: 0, + SrcTopLeftY: srcRow, + SrcBottomRightX: float64(srcWidth), + SrcBottomRightY: srcRow + float64(thisHeight)*resY, + TargetWidth: dstWidth, + TargetHeight: thisHeight, + }) + dstRow += stripHeight + srcRow += float64(stripHeight) * resY + } + return img +} + +type pIFD struct { + IFD + readers []tiff.ReadAtReadSeeker //TODO: close these + origIFDS []*IFD + origMasks []*IFD + ntx, nty int //total number of (256x256) tiles + np int //number of image planes +} + +func unmarshalIFD(ifd tiff.IFD) (IFD, error) { + cifd := IFD{} + err := tiff.UnmarshalIFD(ifd, &cifd) + if err != nil { + return IFD{}, err + } + return cifd, nil +} + +//given a tile inside the main cog, return the strip and the index of the tile inside that strip +func (img Image) tileStripIdx(x, y int) (strip int, stripx, stripy int) { + /* + ntx := (c.cellXSize + c.internalTileSize - 1) / c.internalTileSize + nty := (c.cellYSize + c.internalTileSize - 1) / c.internalTileSize + fx := x / ntx + fy := y / nty + cell = fy*c.nCellsX + fx + cellx = x % ntx + celly = y % nty + return + */ + + ntx := (img.Strips[0].TargetWidth + img.internalTilingWidth - 1) / img.internalTilingWidth + nty := (img.Strips[0].TargetHeight + img.internalTilingHeight - 1) / img.internalTilingHeight + + strip = y / nty + stripx = x % ntx + stripy = y % nty + return +} + +func (t Tiler) AssembleStrips(dstCog io.Writer, srcStrips [][]tiff.ReadAtReadSeeker) error { + pyr := t.Tiling() + mainIFD, err := pyr[0].assembleLevelStrips(srcStrips[0]) + if err != nil { + panic(err) + } + for z, ovrStrips := range srcStrips[1:] { + ovrIFD, err := pyr[z+1].assembleLevelStrips(ovrStrips) + if err != nil { + panic(err) + } + mainIFD.AddOverview(&ovrIFD.IFD) + } + + cfg := DefaultConfig() + cfg.PreloadTiles = t.preloadTiles + if err = cfg.RewriteIFDTree(&mainIFD.IFD, dstCog); err != nil { + return fmt.Errorf("rewrite: %w", err) + } + return nil +} + +func (img Image) assembleLevelStrips(srcStrips []tiff.ReadAtReadSeeker) (*pIFD, error) { + //prepare the main (synthetic, i.e. not tied to an actual file) IFD + pifd := &pIFD{} + pifd.readers = make([]tiff.ReadAtReadSeeker, len(srcStrips)) + pifd.origIFDS = make([]*IFD, len(srcStrips)) + + maintifd, err := tiff.Parse(srcStrips[0], nil, nil) + if err != nil { + return nil, fmt.Errorf("tiff.parse first strip: %w", err) + } + maintifds := maintifd.IFDs() + if len(maintifds) == 0 || len(maintifds) > 2 { + return nil, fmt.Errorf("only one or 2 ifds supported, got %d", len(maintifds)) + } + pifd.IFD, err = unmarshalIFD(maintifds[0]) + if err != nil { + return nil, fmt.Errorf("unmarshal first strip: %w", err) + } + if pifd.SubfileType != 0 { //subfiletype none + return nil, fmt.Errorf("main ifd subfiletype %d != 0", pifd.SubfileType) + } + pifd.TileByteCounts = nil + pifd.TileOffsets = nil + var mifdp *IFD + if len(maintifds) == 2 { + pifd.origMasks = make([]*IFD, len(srcStrips)) + mifd, err := unmarshalIFD(maintifds[1]) + if err != nil { + return nil, fmt.Errorf("unmarshal first strip mask: %w", err) + } + if mifd.SubfileType != 4 { //subfiletype mask + return nil, fmt.Errorf("mask subfiletype %d != 4", mifd.SubfileType) + } + if mifd.NPlanes() != 1 { + return nil, fmt.Errorf("mask nplanes=%d must be exactly 1", mifd.NPlanes()) + } + if mifd.ImageWidth != pifd.ImageWidth || mifd.ImageHeight != pifd.ImageHeight || + mifd.TileHeight != pifd.TileHeight || mifd.TileWidth != pifd.TileWidth { + return nil, fmt.Errorf("mask size/tiling must match main size/tiling") + } + mifd.TileByteCounts = nil + mifd.TileOffsets = nil + mifdp = &mifd + } + pifd.ImageHeight = uint64(img.Height) + pifd.ImageWidth = uint64(img.Width) + pifd.ntx, pifd.nty = pifd.NTilesX(), pifd.NTilesY() + pifd.np = pifd.NPlanes() + nTifTiles := 0 + nTifMaskTiles := 0 + + // plug in the actual cell IFDs + for s, stripReader := range srcStrips { + //avoid variable bug in function closures. + s := s + stripReader := stripReader + + stripReader.Seek(0, io.SeekStart) + pifd.readers[s] = stripReader + tifd, err := tiff.Parse(pifd.readers[s], nil, nil) + if err != nil { + return nil, fmt.Errorf("tiff.parse strip %d: %w", s, err) + } + tifds := tifd.IFDs() + if len(tifds) != len(maintifds) { + return nil, fmt.Errorf("inconsistent ifd/masks") + } + + cifd, err := unmarshalIFD(tifds[0]) + if err != nil { + return nil, fmt.Errorf("unmarshal strip %d: %w", s, err) + } + nTifTiles += len(cifd.TileByteCounts) + pifd.origIFDS[s] = &cifd + if cifd.SubfileType != 0 { //subfiletype none + return nil, fmt.Errorf("BUG: subfiletype of ifd[0] != 0") + } + + cifd.LoadTile = func(idx int, data []byte) error { + if idx >= len(cifd.TileByteCounts) || + len(data) != int(cifd.TileByteCounts[idx]) { + return fmt.Errorf("BUG: len(data)!=TileByteCounts[%d]", idx) + } + _, err = stripReader.ReadAt(data, int64(cifd.TileOffsets[idx])) + if err != nil { + rr := stripReader.(*osio.Reader) + return fmt.Errorf("readat len=%d from %d in file of size %d: %w", + len(data), cifd.TileOffsets[idx], rr.Size(), + err) + } + return nil + } + if len(tifds) == 2 { + mifd, err := unmarshalIFD(tifds[1]) + if err != nil { + return nil, fmt.Errorf("unmarshal mask for strip %d: %w", s, err) + } + pifd.origMasks[s] = &mifd + if mifd.SubfileType != 4 { //subfiletype mask + return nil, fmt.Errorf("mask for strip %d subfiletype != 4", s) + } + nTifMaskTiles += len(mifd.TileByteCounts) + mifd.LoadTile = func(idx int, data []byte) error { + if idx >= len(mifd.TileByteCounts) || + len(data) != int(mifd.TileByteCounts[idx]) { + return fmt.Errorf("BUG: mask len(data)!=TileByteCounts[%d]", idx) + } + _, err = stripReader.ReadAt(data, int64(mifd.TileOffsets[idx])) + return err + } + } + } + if nTifMaskTiles != 0 && nTifTiles/pifd.np != nTifMaskTiles { + return nil, fmt.Errorf("inconsistent mask tile count %d vs %d", nTifMaskTiles, nTifTiles) + } + if pifd.ntx*pifd.nty*pifd.np != nTifTiles { + return nil, fmt.Errorf("inconsistent tile count %dx%dx%d vs %d", pifd.np, pifd.ntx, pifd.nty, nTifTiles) + } + pifd.TileByteCounts = make([]uint64, nTifTiles) + oidx := 0 + for p := 0; p < pifd.np; p++ { + for y := 0; y < pifd.nty; y++ { + for x := 0; x < pifd.ntx; x++ { + s, sx, sy := img.tileStripIdx(x, y) + tidx := pifd.origIFDS[s].TileIdx(sx, sy, p) + pifd.TileByteCounts[oidx] = pifd.origIFDS[s].TileByteCounts[tidx] + oidx++ + } + } + } + pifd.LoadTile = func(idx int, data []byte) error { + x, y, p := pifd.TileFromIdx(idx) + s, sx, sy := img.tileStripIdx(x, y) + sidx := pifd.origIFDS[s].TileIdx(sx, sy, p) + return pifd.origIFDS[s].LoadTile(sidx, data) + } + if mifdp != nil { + mifdp.ImageHeight = pifd.ImageHeight + mifdp.ImageWidth = pifd.ImageWidth + if mifdp.NTilesX()*mifdp.NTilesY() != nTifMaskTiles { + return nil, fmt.Errorf("inconsistent mask tile count %dx%d vs %d", mifdp.NTilesX(), mifdp.NTilesY(), nTifMaskTiles) + } + mifdp.TileByteCounts = make([]uint64, nTifMaskTiles) + ntx, nty := mifdp.NTilesX(), mifdp.NTilesY() + oidx := 0 + for y := 0; y < nty; y++ { + for x := 0; x < ntx; x++ { + s, sx, sy := img.tileStripIdx(x, y) + tidx := pifd.origMasks[s].TileIdx(sx, sy, 0) + mifdp.TileByteCounts[oidx] = pifd.origMasks[s].TileByteCounts[tidx] + oidx++ + } + } + mifdp.LoadTile = func(idx int, data []byte) error { + x, y, p := mifdp.TileFromIdx(idx) + if p != 0 { + return fmt.Errorf("BUG: planeidx %d != 0", p) + } + s, sx, sy := img.tileStripIdx(x, y) + sidx := pifd.origMasks[s].TileIdx(sx, sy, 0) + return pifd.origMasks[s].LoadTile(sidx, data) + } + pifd.AddMask(mifdp) + } + return pifd, nil +} diff --git a/tiler_test.go b/tiler_test.go new file mode 100644 index 0000000..ec81820 --- /dev/null +++ b/tiler_test.go @@ -0,0 +1,34 @@ +package cogger + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestTilerOverviews(t *testing.T) { + testfunc := func(w, h int, expectedlen int) { + t.Helper() + tiler, _ := NewTiler(w, h, InternalTileSize(300, 300), MinOverviewSize(3)) + pyramid := tiler.Tiling() + assert.Len(t, pyramid, expectedlen) + } + cases := [][]int{ + {300, 300, 1}, + {299, 299, 1}, + {301, 301, 2}, + {300, 301, 2}, + {301, 300, 2}, + {301, 4, 2}, + {301, 3, 1}, + {301, 2, 1}, + {4, 301, 2}, + {3, 301, 1}, + {2, 301, 1}, + } + + for _, c := range cases { + testfunc(c[0], c[1], c[2]) + } + //tiler,_=NewTiler(InternalTileSize(10,10),MinOverviewSize(3),OverviewCount() +}